67

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?

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
Hery
  • 7,443
  • 9
  • 36
  • 41

4 Answers4

46

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.

nbro
  • 15,395
  • 32
  • 113
  • 196
digitaljoel
  • 26,265
  • 15
  • 89
  • 115
  • Hmmm if I do as you say (I assume no cascading at all since you don't mention anything), when I have a user assigned to a certain group, what will happen when I delete the user? Will the user-group relationship in join table deleted? Will the group itself be deleted? – Hery Feb 09 '11 at 13:19
  • 2
    if you do it the way I said, the user-group relationship will be entirely managed by the Group side of things. That means you probably won't be able to delete the user at all until after you have removed them from all the groups. In order for that to stick, you will have to remove the user from the group, then save the group. Deleting just the user will not remove the user-group relationship, nor the join table. Without cascade, deleting a group will remove the associations, but not the users, which is what you said you wanted in the original question. – digitaljoel Feb 09 '11 at 16:00
  • 2
    I see... So I have to use the group as the managing side... Is there any way to have both side to be the managing group? In other words, when I delete a user, the user and the user-group will be deleted as well, but not the group and when I delete a group, the group and the user-group will be deleted but not the user. – Hery Feb 10 '11 at 01:10
  • Continued from previous comment. Or only the hard way is available, that is, if the group is the managing side, to delete a user, I have to use the group objects to remove the user first, save the group objects and then finally delete the user? – Hery Feb 10 '11 at 01:12
  • 13
    only one side can be the managing side of any bi-directional relationship. I avoid bi-directional relationships as much as possible because they can be a pain to maintain. Your best bet is to map it on the side that meets your most regular use case, and then wrap the operation in a service method that will take care of making sure everything happens that should, like updating the non-managing side of the relationship etc. – digitaljoel Feb 10 '11 at 05:39
6

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.

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
atidafi
  • 61
  • 1
5

Seeing the problem from another angle:

You could add FK constraints to your many-to-many mapping table. Actually, you should always have FKs in your 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.

Pang
  • 9,564
  • 146
  • 81
  • 122
Pierre Henry
  • 16,658
  • 22
  • 85
  • 105
1

You can do something like this

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