How to remove entity with ManyToMany relationship in JPA (and corresponding join table rows)?


How to remove entity with ManyToMany relationship in JPA (and corresponding join table rows)?



Let's say I have two entities: Group and User. Every user can be member of many groups and every 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 remove a group (let's say it has many members).

Problem is that when I call EntityManager.remove() on some Group, JPA provider (in my case Hibernate) does not remove rows from join table and delete operation fails due to foreign key constrains. Calling remove() on User works fine (I guess this has something to do with owning side of relationship).

So how can I remove a group in this case?

Only way I could come up with is to load all users in the group, then for every user remove current group from his groups and update user. But it seems ridiculous to me to call update() on every user from the group just to be able to delete this group.




Why do we use hibernate annotation?

1:



Need easy way to force all DynaBean property names to lower case

.
entityManager.remove(group) for (User user : group.users) {      user.groups.remove(group); } ... 

// then merge() and flush()


2:


The following works for me.

Add the following method to the entity that is not the owner of the relationship (Group).
@PreRemove private void removeGroupsFromUsers() {     for (User u : users) {         u.getGroups().remove(this);     } } 
Keep in mind that for this to work, the Group must have an updated list of Users (which is not done automatically).

so everytime you add a Group to the group list in User entity, you should also add a User to the user list in the Group entity..


3:


I found a possible solution, but...

I don't know if it's 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 have tried this and it works.

When you delete Role, also the relations are deleted (but not the Permission entities) and when you delete Permission, the relations with Role are deleted too (but not the Role instance).

But we are mapping a unidirectional relation two times and both entities are the owner of the relation.

Could this cause some problems to Hibernate? Which type of problems?. Thanks!. The code above is from another post related..


4:


For what its worth, I am using EclipseLink 2.3.2.v20111125-r10461 and if I have a @ManyToMany unidirectional relationship I observe the problem that you describe.

However, if I change it to be a bi-directional @ManyToMany relationship I am able to delete an entity from the non-owning side and the JOIN table is updated appropriately.

This is all without the use of any cascade attributes..


5:


As an alternative to JPA/Hibernate solutions : you could use a CASCADE DELETE clause in the database definition of your foregin key on your join table, such as (Oracle syntax) :.
CONSTRAINT fk_to_group      FOREIGN KEY (group_id)      REFERENCES group (id)      ON DELETE CASCADE 
That way the DBMS itself automatically deletes the row that points to the group when you delete the group.

And it works whether the delete is made from Hibernate/JPA, JDBC, manually in the DB or any other way.. the cascade delete feature is supported by all major DBMS (Oracle, MySQL, SQL Server, PostgreSQL)..



70 out of 100 based on 80 user ratings 1330 reviews