How to delete an object with ManyToMany relationships in JPA (and the corresponding rows of the connection table)?

Let's say I have two objects: Group and User. Each user can be a member of many groups, and each group can have many users.

@Entity public class User { @ManyToMany Set<Group> groups; //... } @Entity public class Group { @ManyToMany(mappedBy="groups") Set<User> users; //... } 

Now I want to delete the group (for example, it has many members).

The problem is that when I call EntityManager.remove () in any group, the JPA provider (in my case Hibernate) does not delete rows from the connection table , and the delete operation is not performed due to foreign key restrictions. Calling the remove () function for the user works fine (I think this has to do with ownership of the relationship side).

So how can I delete a group in this case?

The only way I could come up with is to upload all users to a group, and then for each user, remove the current group from their groups and update the user. But it seems ridiculous to call update () for each user in the group to remove this group.

+73
java orm hibernate jpa
Jul 04 '09 at 12:13
source share
8 answers
  • Relationship ownership is determined by where you put the "mappedBy" attribute on the annotation. The object you put 'mappedBy' is one that is NOT the owner. Both sides have no chance of becoming owners. If you do not have the precedent of “deleting a user,” you can simply transfer the ownership of the Group object, as the owner is currently User .
  • On the other hand, you did not ask about this, but you need to know one thing. groups and users not combined with each other. I mean, after deleting an instance of User1 from Group1.users, the collections of User1.groups do not change automatically (which is completely unexpected for me),
  • In general, I would suggest you decide who owns it. Let's say User is the owner. Then, when the user is deleted, the user relationship group will be automatically updated. But when you delete a group, you must take care of deleting the relationship yourself:



 entityManager.remove(group) for (User user : group.users) { user.groups.remove(group); } ... // then merge() and flush() 
+69
Jul 06 '09 at 20:15
source share

The following works for me. Add the following object to an object that does not own the relationship (Group)

 @PreRemove private void removeGroupsFromUsers() { for (User u : users) { u.getGroups().remove(this); } } 

Keep in mind that this requires the Group to have an updated list of users (this is not done automatically). therefore, every time you add a group to the list of groups in the user entity, you must also add the user to the list of users in the Group object.

+35
Feb 16 '13 at 15:30
source share

I found a possible solution, but ... I do not know if this is a good solution.

 @Entity public class Role extends Identifiable { @ManyToMany(cascade ={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable(name="Role_Permission", joinColumns=@JoinColumn(name="Role_id"), inverseJoinColumns=@JoinColumn(name="Permission_id") ) public List<Permission> getPermissions() { return permissions; } public void setPermissions(List<Permission> permissions) { this.permissions = permissions; } } @Entity public class Permission extends Identifiable { @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable(name="Role_Permission", joinColumns=@JoinColumn(name="Permission_id"), inverseJoinColumns=@JoinColumn(name="Role_id") ) public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } 

I tried this and it works. When you delete a Role, relations (but not Permission objects) are also deleted, and when you delete Permission, relations with a role are also deleted (but not an instance of Role). But we map the unidirectional relationship two times, and both objects are the owners of the relationship. Could this cause some problems for hibernation? What type of problems?

Thank!

The code above refers to another post.

+22
Aug 26 '10 at 11:59 on
source share

As an alternative to JPA / Hibernate solutions, you can use the CASCADE DELETE clause to define the database of your foregin key in your connection table, for example (Oracle syntax):

 CONSTRAINT fk_to_group FOREIGN KEY (group_id) REFERENCES group (id) ON DELETE CASCADE 

Thus, the DBMS automatically deletes the line pointing to the group when the group is deleted. And it works whether the deletion from Hibernate / JPA, JDBC is performed manually in the database or in any other way.

The cascade removal function is supported by all major DBMSs (Oracle, MySQL, SQL Server, PostgreSQL).

+6
Jun 29 '15 at 9:50
source share

For what it's worth, I'm using EclipseLink 2.3.2.v20111125-r10461, and if I have a unidirectional @ManyToMany relationship, I observe the problem you described. However, if I change it to the @ManyToMany bidirectional relation, I can delete the object from the non-owner side, and the JOIN table is updated accordingly. This is all without using any cascading attributes.

+3
Jun 02 '12 at 3:21
source share

This is a good decision. The best part on the SQL side is that fine tuning to any level is simple.

I used MySql and MySql Workbench for Cascade to delete for the required foreign key.

 ALTER TABLE schema.joined_table ADD CONSTRAINT UniqueKey FOREIGN KEY (key2) REFERENCES schema.table1 (id) ON DELETE CASCADE; 
+1
Jul 08 '17 at 20:09 on
source share

In my case, I deleted mappedBy and joined the tables as follows:

 @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "user_group", joinColumns = { @JoinColumn(name = "user", referencedColumnName = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "group", referencedColumnName = "group_id") }) private List<User> users; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JsonIgnore private List<Group> groups; 
+1
Jan 10 '19 at 0:32
source share

This works for me:

 @Transactional public void remove(Integer groupId) { Group group = groupRepository.findOne(groupId); group.getUsers().removeAll(group.getUsers()); // Other business logic groupRepository.delete(group); } 

Also, check the @Transactional method (org.springframework.transaction.annotation.Transactional), this will make the whole process in one session, save some time.

0
Mar 06 '18 at 21:11
source share



All Articles