Enregistrement de ManyToMany bidirectionnel
Question
J'ai deux classes d'entités annotées de la manière suivante
@Entity
class A {
@ManyToMany(mappedBy="A", cascade=CascadeType.ALL)
private List<B> b;
..
}
@Entity
class B {
@ManyToMany(cascade=CascadeType.ALL)
private List<A> a;
..
}
Si je stocke une instance de la classe 'B', les relations sont stockées dans la base de données et le getter de la classe 'A' renverra le sous-ensemble correct de B's. Cependant, si je modifie la liste de B en "A", les modifications ne sont pas stockées dans la base de données?
Ma question est la suivante: comment puis-je faire en sorte que les modifications de l'une ou l'autre classe soient "en cascade"? à l'autre classe?
EDIT: j'ai essayé différentes variantes de suppression du paramètre mappedBy et de définition d'un JoinTable (et de colonnes), mais je n'ai pas trouvé la bonne combinaison.
La solution
La réponse la plus courte semble être que vous ne pouvez pas et que cela a du sens. Dans une association bidirectionnelle plusieurs à plusieurs, un côté doit être maître et est utilisé pour conserver les modifications apportées à la table de jointure sous-jacente. Comme JPA ne gère pas les deux côtés de l'association, vous risquez de vous retrouver avec une situation de mémoire qui ne pourrait pas être rechargée une fois stockée dans la base de données. Exemple:
A a1 = new A();
A a2 = new A();
B b = new B();
a1.getB().add(b);
b.getA().add(a2);
Si cet état pouvait être conservé, vous vous retrouveriez avec les entrées suivantes dans la table de jointure:
a1_id, b_id
a2_id, b_id
Mais lors du chargement, comment JPA aurait-il su que vous aviez l’intention de ne laisser savoir que b à propos de a2 et non a1? et que dire de a2 qui ne devrait pas savoir à propos de b?
C’est pourquoi vous devez conserver vous-même l’association bidirectionnelle (et rendre l’exemple ci-dessus impossible à obtenir, même en mémoire) et JPA ne persistera que sur la base de l’état de l’un de ses côtés.
Autres conseils
Avez-vous spécifié les colonnes de jointure inverse?
@Entity
class A {
@ManyToMany(mappedBy="A", cascade=CascadeType.ALL)
private List <B> b;
..
}
@Entity
class B {
@ManyToMany
@JoinTable (
name="A_B",
joinColumns = {@JoinColumn(name="A_ID")},
inverseJoinColumns = {@JoinColumn(name="B_ID")}
)
private List<A> a;
..
}
Cela suppose une table de jointure appelée A_B avec les colonnes A_ID et B_ID.
Comme la relation est bidirectionnelle, de même que les mises à jour de l'application un côté de la relation, l’autre côté devrait également être mis à jour, et être en phase. En JPA, comme en Java en général , c’est le la responsabilité de l'application, ou le modèle objet de maintenir relations. Si votre application ajoute à l'un des côtés d'un relation, alors il doit ajouter à l'autre côté.
Ceci peut être résolu en ajoutant ou en définissant des méthodes dans le modèle d'objet qui gèrent les deux côtés des relations, de sorte que le code de l'application n'a pas besoin de s'inquiéter à ce sujet. Il y a deux façons d'aborder cela, vous pouvez uniquement ajouter le code de maintenance de relation sur un côté de la relation et n'utilisez le passeur que d'un côté (comme en protégeant l’autre côté), ou ajoutez-le des deux côtés et assurez-vous vous évitez une boucle infinie.
Source: OneToMany # Getters_and_Setters
Avez-vous essayé d’ajouter le paramètre mappedBy au champ A de la classe B comme suit
@Entity
class B {
@ManyToMany(cascade=CascadeType.ALL, mappedBy = "b")
private List<A> a;
..
}
Peut-être qu'il y a une petite erreur dans la réponse de A_M. À mon avis, cela devrait être:
@Entity class B { @ManyToMany @JoinTable ( name="A_B", joinColumns = {@JoinColumn(name="B_ID")}, inverseJoinColumns = {@JoinColumn(name="A_ID")} ) private List a; .. }