web-dev-qa-db-fra.com

JPA Hibernate en cascade plusieurs-à-plusieurs

J'utilise JPA 2.0 et je mets en veille prolongée. J'ai une classe Utilisateur et une classe Groupe comme suit:

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
}

Et puis, je crée un utilisateur et un groupe, puis j'affecte l'utilisateur au groupe.

Ce que je veux, c'est quand je supprime le groupe, le groupe sera supprimé (bien sûr) et toute la relation utilisateur-groupe que le groupe a sera automatiquement supprimée de la table de jointure USER_GROUP mais l'utilisateur lui-même n'est pas supprimé de la Table USER.

Avec le code que j'ai ci-dessus, seule la ligne de la table GROUP sera supprimée lorsque je supprimerai un groupe et l'utilisateur aura toujours une entrée dans le groupe supprimé dans la table de jointure USER_GROUP.

Si je mets cascade dans la classe User comme ceci:

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

Lorsque je supprime le groupe, l'utilisateur sera également supprimé!

Existe-t-il un moyen de réaliser ce que je veux?

48
Hery

De la façon dont vous l'avez mappé, le User est le côté gestionnaire de la relation, il sera donc responsable de la mise à jour de la table de jointure.

Modifiez le mappage JoinTable de User à Group, et définissez la propriété groupList de User afin qu'elle ait le mappedBy attribut. Cela changera le groupe du côté gestion de la relation et fera des appels de persistance/mise à jour au groupe pour gérer la table de jointure.

Mais prenez note de cela, vous ne pourrez pas simplement ajouter un groupe à un utilisateur, enregistrer l'utilisateur et continuer, vous devrez plutôt ajouter un utilisateur au groupe et enregistrer le groupe pour voir les modifications, mais ayant le plusieurs-à-plusieurs bidirectionnel, nous espérons que vous gérerez de près cette relation de toute façon.

38
digitaljoel

Voir le problème sous un autre angle:

Vous pouvez ajouter des contraintes FK à votre table de mappage plusieurs à plusieurs. En fait, vous devriez toujours avoir des FK dans votre base de données pour des raisons d'intégrité des données. Dans ce cas par exemple, au moins l'utilisateur n'a pas pu être supprimé tout en laissant silencieusement des lignes orphelines dans la table de mappage.

Lorsque vous avez des FK, vous pouvez spécifier on supprimer l'action référentielle en cascade sur les contraintes, et la base de données gérera la suppression automatique de la table de mappage, mais pas de l'entité, comme vous l'avez demandé.

5
Pierre Henry

Comme déjà indiqué, définissez le groupe comme propriétaire de la relation si vous n'avez pas besoin de faire une cascade du côté utilisateur.

Ensuite, essayez d'utiliser cascade=CascadeType.MERGE pour ne pas supprimer en cascade l'utilisateur lorsque vous supprimez le groupe.

4
atidafi

Vous pouvez faire quelque chose comme ça

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