Model relationship "User has different Roles per Organization" - java

I'm trying to achieve the following with JPA: I have Users, Organizations and Roles. A User can have multiple Roles in a given Organization. He can also belong to multiple Organizations, and of course have different Roles per Organization.
Currently I would think that a schema for this should look like this (but also open to alternative aproaches):
CREATE TABLE user
(
id INT NOT NULL AUTO_INCREMENT
);
CREATE TABLE role
(
id INT NOT NULL AUTO_INCREMENT
);
CREATE TABLE organization
(
id INT NOT NULL AUTO_INCREMENT
);
CREATE TABLE `user_and_organization_to_role`
(
`id` INT NOT NULL AUTO_INCREMENT,
`fk_user` INT NOT NULL REFERENCES user (id),
`fk_organization` INT NOT NULL REFERENCES organization (id),
`fk_role` INT NOT NULL REFERENCES role (id),
PRIMARY KEY (`id`),
UNIQUE KEY (`fk_user`, `fk_organization`, `fk_role`)
);
I wouldn't have problems checking roles with native SQL Queries, but I would like to model this in JPA to use the Hibernate Metamodel and Criteria API to implement permission checks.
I thought that something like this would be achievable, even though I'm not 100% sure if I'll reach my goal with Criteria API then:
#Entity
public class Organization {
}
#Entity
public class Role {
}
#Entity
public class User {
private Map<Organization, List<Role>> organizationToRoles;
}
Unfortunately I didn't manage to find a way for the correct annotation, so organizationToRoles is mapped correctly. And even though I would think that is a common problem I didn't find a tutorial that explains how to do this.
Could somebody tell me if such a map is doable with JPA at all, and maybe give an example?
Or if it is not possible to directly have a Map<Organization, List<Role>> organizationToRoles in User, how a mapping could be achived, e.g. with an intermediate Entity that forms the relation between User, Organization and Roles?

Related

Why is a entity - value relationship implemented as a back reference in Spring Data JDBC

In Spring Data JDBC if an entity (e.g. Customer) has a value (e.g. Address) like in the example here the value has a back reference column (column customer in table address) to the entity in the db schema:
CREATE TABLE "customer" (
"id" BIGSERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE "address" (
"customer" BIGINT,
"city" VARCHAR(255) NOT NULL
);
The problem with this is that if you use that Address value more than once in one entity or even in different entities you have to define an extra column for each usage. Only the primary id of the entity is stored in these columns and otherwise there is no way to distinguish from which entity it is. In my actual implementation I have five of these columns for the Address value:
"order_address" BIGINT, -- backreference for orderAddress to customer id
"service_address" BIGINT, -- backreference for serviceAddress to customer id
"delivery_address" BIGINT, -- backreference for deliveryAddress to customer id
"installation_address" BIGINT, -- backreference for installationAddress to provider_change id
"account_address" BIGINT, -- backreference for accountAddress to payment id
I understand how it works, but I don't understand the idea behind this back reference implementation. So can someone please shed some light on that issue? Thanks!
As to most good questions there are many sides to the answer.
The historical/symmetry answer
When it comes to references between entities Spring Data JDBC supports 1:1 (the one you ask about) and 1:N (lists, sets and maps).
For the latter anything but a back-reference is just weird/wrong.
And with using a back-reference for 1:1 becomes basically the same, simplifying the code, which is a good thing.
The DML process answer
With the back-reference, the process of inserting and deleting becomes much easier: Insert the aggregate root (customer in your example) first, then all the referenced entities. And it continues to work if those entities have further entities. Deletes work the other way round but are equally straight forward.
The dependency answer
Referenced entities in an aggregate can only exist as part of that aggregate. In that sense they depend on the aggregate root. Without that aggregate root there is no inner entity, while the aggregate root very often might just as well exist without the inner entity. It therefore makes sense, that the inner entity carries the reference.
The ID answer
With this design, the inner entity doesn't even need an id. It's identity is perfectly given by the identity of the aggregate root and in case of multiple one-to-one relationships to the same entity class, the back-reference column used.
Alternatives
All the reasons are more or less based on a single one-to-one relationship. I certainly agree that it looks a little weird for two such relationships to the same class and with 5 as in your example it becomes ridiculous. In such cases you might want to look in alternatives:
Use a map
Instead of modelling your Customer class like this:
class Customer {
#Id
Long id;
String name;
Address orderAddress
Address serviceAddress
Address deliveryAddress
Address installationAddress
Address accountAddress
}
Use a map like this
class Customer {
#Id
Long id;
String name;
Map<String,Address> addresses
}
Which would result in an address table like so
CREATE TABLE "address" (
"customer" BIGINT,
"customer_key" VARCHAR(20). NOT NULL,
"city" VARCHAR(255) NOT NULL
);
You may control the column names with a #MappedCollection annotation and you may add transient getter and setter for individual addresses if you want.
Make it a true value
You refer to Address as a value while I referred to it as an entity. If it should be considered a value I think you should map it as an embedded like so
class Customer {
#Id
Long id;
String name;
#Embedded(onEmpty = USE_NULL, prefix="order_")
Address orderAddress
#Embedded(onEmpty = USE_NULL, prefix="service_")
Address serviceAddress
#Embedded(onEmpty = USE_NULL, prefix="delivery_")
Address deliveryAddress
#Embedded(onEmpty = USE_NULL, prefix="installation_")
Address installationAddress
#Embedded(onEmpty = USE_NULL, prefix="account_")
Address accountAddress
}
This would make the address table superfluous since the data would be folded into the customer table:
CREATE TABLE "customer" (
"id" BIGSERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
"order_city" VARCHAR(255) NOT NULL,
"service_city" VARCHAR(255) NOT NULL,
"deliver_city" VARCHAR(255) NOT NULL,
"installation_city" VARCHAR(255) NOT NULL,
"account_city" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
Or is it an aggregate?
But maybe you need addresses on their own, not as part of a customer.
If that is the case an address is its own aggregate.
And references between aggregates should be modelled as ids or AggregateReference. This is described in more detail in Spring Data JDBC, References, and Aggregates

Entity class with a composite primary key doesn't have getter and setter

I have an entity class userdetails which has the username, userid (numeric) and password fields, with username and userid forming a composite primary key. This is negotiable, and possibly unimportant to the main problem.
I have another class, connectiontable, which has userid as the primary key. The sql code used to generate the relevant tables is as follows:
create table usertable
(
userid int NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
username varchar(128) NOT NULL UNIQUE,
password varchar(128) NOT NULL,
CONSTRAINT USER_PK PRIMARY KEY(username, userid)
);
That's the sql code for usertable. The following is for connectiontable
create table connectiontable
(
userid int not null,
username varchar(128) not null,
connections varchar(32670) not null,
CONSTRAINT CONNECTION_PK PRIMARY KEY(username, userid),
CONSTRAINT CONNECTION_FK FOREIGN KEY(username,userid) REFERENCES usertable(username,userid)
);
There are a bunch of other things in connectiontable, but those are irrelevant. I use netbeans 7.2.1 and Jave EE6. I use the 'create entities from database entries' but for some reason, I don't have a getter and setter for either userid or username. They are in connectiontablePK, but I can't seem to make use of that. For example, when I generate the jsf pages, I want to be able to do something like:
Connectiontable con = new Connectiontable();
con.getUsername();
But it complains because it can't find that method in connectiontable.java.
Can anyone advise me why this is the case, and how I can solve it? Thank you.
... You're not posting the Java code, which I suspect would help but:
Anytime in JPA when you have a composite primary key, you have to have an 'embedded' primary-key class. I suspect you have a class definition similar to the following:
#Embeddable
public class UserNameId {
private int userid;
private String username;
}
And then usertable and connectiontable both contain the following (or similar):
#Embedded
#Id
private UserNameId userNameId;
... So you should expect a getter/setter for userNameId, but not the embedded fields, like you expect.

JPA 2: Map Mapping fails for duplicate values

I'm trying to map a HashMap similar to the one that is specified as example 3 in the JavaDoc for #MapKeyJoinColumn (see http://www.objectdb.com/api/java/jpa/MapKeyJoinColumn):
#Entity
public class Student {
#Id int studentId;
...
#ManyToMany // students and courses are also many-many
#JoinTable(name="ENROLLMENTS",
joinColumns=#JoinColumn(name="STUDENT"),
inverseJoinColumns=#JoinColumn(name="SEMESTER"))
#MapKeyJoinColumn(name="COURSE")
Map<Course, Semester> enrollment;
...
}
The generated join table (generated with EclipseLink 2.3) has the following layout:
TABLE enrollments (
student_id bigint NOT NULL,
semester_id bigint NOT NULL,
course_id bigint,
CONSTRAINT enrollments_pkey PRIMARY KEY (student_id, semester_id)
)
Why is the primary key generated for Student and Semester and not for Student and Course? This doesn't make any sense in this case. With this primary key, a Student can participate in only one course per semester. 'student_id' and 'course_id' should be defined as primary key! This would also match the Java map definition (the key must be unique, but the same value may be assigned to different keys)
JPA sees the relationship as being between Student and Semester, as in a traditional #ManyToMany without the #MapKeyJoinColumn, and in traditional #ManyToMany duplicates would not be allowed, and the items are deleted by source/target ids, so the pk/index is desired to be on these.
For a finer level of control of the model, consider mapping the ENROLLMENTS table to an Enrollment Entity instead.
I can see from the Java model how you may desire different, so please log a bug/enhancement for this.

How to decouple database entity dependency in SOA?

I have two services: TeacherService and PupilSerivce which are sharing the same database. The relationship between them is one-to-many that says one teacher can have many pupils and each pupil only has one teacher.
CREATE TABLE `test`.`teacher` {
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(40),
PRIMARY KEY (`id`)
} ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `test`.`pupil` {
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`teacher_id` bigint unsigned NOT NULL COMMENT 'teacher id',
PRIMARY KEY (`id`),
CONSTRAINT `fk_teacher_id` FOREIGN KEY (`teacher_id`) REFERENCES `teacher`(`id`) ON DELETE CASCADE
} ENGINE=InnoDB DEFAULT CHARSET=utf8;
as you can imagine, I have two entities TeacherVO and PupilVO that represents database table in my Java code.
the question is, both TeacherService and PupilSerivce run in individual process and communicate with messages, I don't want they have any compilation dependency on each other. However, when adding a new pupile the method looks like:
PupilService.addNewPupil(long teacherId) {
if (isValidTeacher(teacherId) {
// add the pupile
}
}
this requires PupileService has knowledge of TeacherVO so it can do some validation, but TeacherVO is in TeacherService package!
What's the best practice to remove such sort of dependency?
There are some ways I thought of:
create a validateTeacher message, then PupilService sends this message to TeacherService and wait for response. However, if I have further requirements like searching a teacher with name, then I have to create another message which at last results in message blowing up. Directly searching database is a more flexible and efficient way, but it introduces dependency.
don't do any check and catch SQL exception caused by foreign key if the teacher_id is invalid. however, this can not solve the problem that I may have further requirements.
I think this should be a common issue in SOA architecture in case of multiple services share the same database. I did research a while but not get anything valuable.
I agree with Tony Hopkinson, what you're trying to do is fundamentally unsound. You cannot separate the concerns of the teacher and student.

MYSQL stored procedure accessing java object stored as a BLOB

I am storing a Java object as an byte in a blob of a table. The java object is customized object. How can I construct the java object and use it in the stored procedure?
Let the class implement java.io.Serializable so that you can get an InputStream of it which you can store in the DB using CallableStatement#setBinaryStream().
That said, this is usually considered a bad design. If the class is actually a Javabean class, you'd better create a table with columns which represents the Javabean properties. E.g. a public class User { private Long id; private String name; private Integer age; } should be mapped to a table like CREATE TABLE user ( id BIGINT AUTO_INCREMENT, name VARCHAR, age INTEGER )
Edit as a reply on your comment: you thus basically want to store an array as binary object. This is a very bad idea. This way you cannot search for the array's data in the database and the database would also not be portable anymore. Just create a new table which represents each of the array items. Add an extra column to it which represents the ID of the parent object (actually, it should be the PK of the table to which the parent object containing the array is been mapped.
Example:
public class Parent {
private Long id;
private String someData;
private List<Child> children;
// Add/generate public getters/setters.
}
public class Child {
private Long id;
private String someData;
// Add/generate public getters/setters.
}
should be mapped to
CREATE TABLE parent (
id BIGINT NOT NULL AUTO_INCREMENT,
someData VARCHAR,
PRIMARY KEY (id)
);
CREATE TABLE child (
id BIGINT NOT NULL AUTO_INCREMENT,
parent_id BIGINT NOT NULL,
someData VARCHAR,
PRIMARY KEY (id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
);
this way you can just select all with help of a JOIN clause. Check the SQL tutorial at w3schools.com and the vendor-specific SQL documentation for examples.
How can I construct the java object and use it in the stored procedure?
This is not possible, at least not with MySQL. Unlike Oracle which supports Java Stored Procedures, MySQL's stored procedure syntax is based on plain ANSI SQL standard. So I don't see how you could construct a Java object from the stream stored in the BLOB. What you can do is acces to the BLOB, but this won't help you much IMHO.
Actually, I think you are totally on the wrong path here, using a BLOB is not the right way to go (at least not here). If you need to persist objects that have a 1:n relation between them, you need to model your database accordingly.
If your Record class has a one to many relation with the User class, which is my understanding, then you have something like this on the Java side:
public class Record {
private Long id;
private User[];
//...
}
Then you need to create two tables at the database level, one for the records and another for the user(s), and model the relation between them using a foreign key (so you can "attach" a user to a record):
CREATE TABLE record
(
record_id INT NOT NULL,
...,
PRIMARY KEY (record_id)
) TYPE = INNODB;
CREATE TABLE user
(
user_id INT NOT NULL,
record_id INT,
...
PRIMARY KEY (user_id),
INDEX (record_id),
FOREIGN KEY (user_id) REFERENCES record (record_id)
) TYPE = INNODB;
Finally, when persisting a Record instance from Java, you'll need to write state in both tables.

Categories