Many To Many JPA Annotations more than two tables - jpa

Currently I have three tables.
User, Role and Institution.
As you know user and roles has many to many relation. (One user can have multiple roles, one role can have multiple users).
Create a many to many annotations as follows.
#In User Table.
#ManyToMany
#JoinTable(name = "USER_ROLES",
joinColumns = {#JoinColumn(name = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID")}
)
private Set<Role> roles = new HashSet<Role>();
#In Role Table.
#ManyToMany
#JoinTable(name = "USER_ROLES",
joinColumns = {#JoinColumn(name = "ROLE__ID")},
inverseJoinColumns = {#JoinColumn(name = "USER_ID")}
)
private Set<User> users = new HashSet<User>();
But later I have realized that a user can have a role in one institution and a different role in another Institution or the same in different institutions. How can I set that relation?

First of all, your simple many to many mapping is wrong. One side of the bidirectional association must be the inverse side, using the mappedBy attribute. For example:
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<User>();
Now to answer your question, you just need one more entity. Let's call it Participation. A participation contains a role of a given user in a given institution. So, you have the following locical associations:
user - OneToMany - participation
participation - ManyToOne - role
participation - ManyToOne - institution

Related

JHipster 4.14.4 : Registering a user with additional information

Following the steps described here I get an org.hibernate.cfg.RecoverableException: Unable to find column with logical name: id in org.hibernate.mapping.Table(user_extra) and its related supertables and secondary tables. It doesn't matter, if I change the entries in the liquibase changelogs regarding "user_extra.user_id". Even if I erase the liquibase changelogs (without the initial_schema) and/or remove the h2-database ( with ./gradlew clean) - every time I get the error.
The link mentioned above is close to the tipp on the jhipster.tech documentation, so I think I am missing something, but I am searching since hours for my failure(s)...
How does jhipster know to map the jhi_user.id to user_extra.user_id (OneToOne) if there is no explicit declaration for this in UserExtra.json? Is the declaration in the domain object UserExtra.java with
#OneToOne
#MapsId
private User user;
enough to trigger it?
Can somebody give me a hint, where Spring Data JPA maps the user_id from the user_extra-table to the #Id Long id of the domain object UserExtra?
My code generated with JHipster 4.14.4 is on github.
Please clarify my questions above, if you like!
Nevertheless I have found my mistakes. Unfortunately, there are no additional ManyToMany-relationships to UserExtra mentioned in the jhipster.tech-tipp - I had to change the user_extra.id to user_extra.user_id not only in the liquibase-changelogs, but also in the domain classes, which are linked ManyToMany to Spring MVC domain class UserExtra!
I faced the same issue, and Based on Jochen Gebsattel's answer, this is what I did:
Update the 202xxxxx_added_entity_constraints_UserExtra.xml
where I had
referencedColumnNames="id"
referencedTableName="user_extra"
I replaced by
referencedColumnNames="user_id"
referencedTableName="user_extra"
user_id being the name of the column containing the foreign key, in my user_extra entity/table
Update the UserExtra class (here again, replace id by user_id)
Where I had many to many relations with other entities, for example
#JoinTable(name = "user_extra_store",
joinColumns = #JoinColumn(name = "user_extra_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "store_id", referencedColumnName = "id"))
I replaced with
#JoinTable(name = "user_extra_store",
joinColumns = #JoinColumn(name = "user_extra_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "store_id", referencedColumnName = "id"))
Note: If you created a new liquibase changelog file in step 1 (instead of update), don't forget to reference it in the master.xml

JPA one-to-one bidirectional mapping on same object using association table

I have a table Person and now I want to express a relation like "best friend". Assuming a person can only have one best friend I don't want to alter the Person table to add a best friend column, rather I want to have an additional mapping table, e.g.:
Table Person (id name):
1 foo
2 bar
3 somebody
4 somebodyelse
Table BestFriendMapping (personId bestfriendId):
1 2
3 4
I was doing something like this:
class Person {
#OneToOne()
#Fetch(FetchMode.SELECT)
#JoinTable(name = "BestFriendMapping",
joinColumns = #JoinColumn(name = "personId", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "bestfriendId",
referencedColumnName = "id"))
private Person bestFriend;
}
The problem is, that now when I add a new Person, the mapping table is populated with two entries, for example the newly added Person is having id=10 and his bestFriend 20, then the entries are:
10 20
20 10
I would like to have just one entry, but still be able to get the best friend of a person no matter which I have in my hand currently. I found out that I probably have done two unidirectional instead of one bi-directional mapping, so I have to use mappedBy, but I am not sure what is the syntax when it is about the one and the same entity object, thus one and the same field inside the object. The examples on the internet are always showing the mapping of two entities via a mapping table.
Or maybe something like this?!? In addition to the JoinColumns and InverseJoinColumns to add mappedBy to the OnetoOne just like this #OneToOne(mappedBy="bestFriend"), kind of weird :)

Bidirectional Many To Many : how to add a user to a group?

hi I have a #ManyToMany relationship (Users and Groups)
in the Group entity I have :
#ManyToMany(fetch=FetchType.EAGER , cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "group_user",
joinColumns = #JoinColumn(name = "gid"),
inverseJoinColumns = #JoinColumn(name = "uid"))
#JsonIgnore
private List<User> users;
in the User entity I have :
#ManyToMany(mappedBy = "users",fetch=FetchType.LAZY , cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JsonIgnore
private List<Group> groups;
the table "group_user" has been created . I can get the list of users in a group and the list of groups to which a user belongs using these queries :
#Query("select g From Group g join g.users u where u.id=?1")
Iterable<Group> getAllGroupsofUser(long idUser);
#Query("select u From User u join u.groups g where g.id=?1")
Iterable<User> getAllUsersingroup(long idGroup);
I want to add a user to a group so I did this
User user=userRepository.findOne(1);
Group group=groupRepository.findOne(3);
group.getUsers().add(user);
groupRepository.save(group);
this works . it adds a new line in my table group_user.
Actually it insert all the new list of users ( it's a problem ) How to fix that ?
Also I need to do the same when I change fetch=FetchType.EAGER to fetch=FetchType.LAZY in my Group entity .
I'm still waiting for an answer ! I need a better solution for adding a user to a group without having to insert all the list again

Prevent Concurrent Modification. Eclipselink (OptimisticLockException)

Problem:
Two Admins -> Admin1 and Admin2 Simultaneously modifying UserA from different terminals.
There is a many-to-many relation between User and UserGroup entity.
Here what is happening is when Admin1 assigns Group1 to UserA, the information is saved.
And when Admin2 from different terminal assigns Group2 to UserA, the information saved but overwrites the changes made by Admin1. Means when I check in DB, UserA is assigned to Group2 while Admin2 is not aware of changes made by Admin1.
Is this Optimistic Situation?
If this is optimistic situation then why this is not throwing OptimisticLockException?
How would I notify Admin2 about the changes made by Admin1.
Using Eclipselink as below:
#ManyToMany
#JoinTable(name = "user_group", joinColumns = { #JoinColumn(name = "user_group_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") })
private List<User> userList;
#ManyToMany
#JoinTable(name = "user_group",joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "user_group_id", referencedColumnName = "id") })
private List<UserGroup> userGroupList;
#Version annotation is marked over version column which is updated on each operation.
Let know if you require more information from my side.
When group1 is assigned to user1, save last modified timestamp. Next time when the request comes to get the user1 details make sure you provide last modified date along with other details. When admin1 modifies the request ask him to provide the same last modified date which you supplied during get operation. Compare the last modified date s same as what you provided to admin1 before saving the requestrequest. Basically here last modified date is kind of token. You store a token, provide the same to user accessing the data and then when user modifies the data and sends it for persist operation make sure token in the database matches the one user has sent back to you else it means that the record was modifed and the token is changed in databse and dont allow the user to persist bad data.
Hope this helps.

jpa OneToMany & ManyToOne

i have a oneToMany and ManyToOne mapping in my models:
class User
#OneToMany (cascade = { CascadeType.REFRESH, CascadeType.DETACH }, fetch = FetchType.EAGER)
private Set<Judgement> judgements;
class Judgement
#ManyToOne(cascade = { CascadeType.REFRESH, CascadeType.DETACH})
#JoinColumn(name = "user_id")
private User judge;
and in DB, i have to tables as Users and Judgements, when i tried to run my code, it showed error as:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "users_judgements" does not exist
does that mean i have to create the table users_judgements by hand, jpa cannot automatically create the relationship for me? RoR can do it...Thanks.
If you have a foreign key from USER table, user_id, in JUDGMENT table then there is no need to have another table. That will be the #JoinColumn(name = "user_id").
See if you are missing something; I can not see any "mappedBy" attribute, which will be "user" in your case.
Please take a look at this article; page 4 that the link will take you, give details about one-to-many relationship. It will be worth reading the whole article.
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa2.html?page=4
Also, the part 1 of the same tutorial is good for basics;
http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa1.html