JPA Hibernate many-to-many cascading

HibernateJpaMany to-ManyJpa 2.0Cascade

Hibernate Problem Overview


I am using JPA 2.0 and hibernate. I have a User class and a Group class as follows:

public class User implements Serializable {
    @Id
    @Column(name="USER_ID")
    private String userId;

    @ManyToMany
    @JoinTable(name = "USER_GROUP",
               joinColumns = {
                   @JoinColumn(name = "GROUP_ID")
               },
               inverseJoinColumns = {
                   @JoinColumn(name = "USER_ID")
               }
    )
    private Set<Group> groupList;
    
    //get set methods
}

public class Group
{
    @Id
    @Column(name="GROUP_ID")
    private String groupId;

    @ManyToMany(mappedBy="groupList")
    private Set<User> memberList;
    //get set methods
}

And then, I create a user and group and then assign the user to the group.

What I want to have is when I delete the group, the group will be deleted (of course) and all the user-group relationship that the group has will be automatically deleted from the USER_GROUP join table but the user itself is not deleted from the USER table.

With the code I have above, only the row in the GROUP table will be deleted when I delete a group and the user will still have an entry to the deleted group in the USER_GROUP join table.

If I put cascade in the User class like this:

@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "USER_GROUP",
joinColumns =
{
    @JoinColumn(name = "GROUP_ID")
},
inverseJoinColumns =
{
    @JoinColumn(name = "USER_ID")
})
private Set<Group> groupList;

When I delete the group, the user will be deleted as well!

Is there any way to achieve what I want?

Hibernate Solutions


Solution 1 - Hibernate

The way you have it mapped, the User is the managing side of the relationship, therefore it will be responsible for updating the join table.

Change the JoinTable mapping from User to Group, and make the groupList property of User so it has the mappedBy attribute. That will change the group to the managing side of the relationship, and make persist/update calls to the group manage the join table.

But take note of that, you won't be able to simply add a group to a user, save the user, and continue, you'll instead have to add a user to the group and save the group to see the changes, but having the bi-directional many-to-many hopefully you'll be closely managing that relationship anyway.

Solution 2 - Hibernate

As already indicated, make the group owner of the relation if you don't need to cascade from the user side.

Then, try to use cascade=CascadeType.MERGE to not delete cascade the user when you delete the group.

Solution 3 - Hibernate

Seeing the problem from another angle :

You could add FK constraints to you many-to-many mapping table. Actually you should always have FKs in you DB for data integrity reasons. In this case for example, at least the user could not be deleted while silently leaving orphan rows in the mapping table.

When you have FKs, you can specify the on delete cascade referential action on the constraints, and the DB will manage the automatic deleting of the mapping table, but not of the entity, like you asked for.

Solution 4 - Hibernate

You can do something like this

@PreRemove
private void removeMembers() {
    this.memberList.clear();
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHeryView Question on Stackoverflow
Solution 1 - HibernatedigitaljoelView Answer on Stackoverflow
Solution 2 - HibernateatidafiView Answer on Stackoverflow
Solution 3 - HibernatePierre HenryView Answer on Stackoverflow
Solution 4 - HibernateVicenzo MartinelliView Answer on Stackoverflow