Oui, c'est possible. Il s'agit d'un cas particulier de la relation bidirectionnelle @ManyToOne
/ standard @OneToMany
. C'est spécial parce que l'entité à chaque extrémité de la relation est la même. Le cas général est détaillé dans la section 2.10.2 de la spécification JPA 2.0 .
Voici un exemple travaillé. Tout d'abord, la classe d'entité A
:
@Entity
public class A implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@ManyToOne
private A parent;
@OneToMany(mappedBy="parent")
private Collection<A> children;
// Getters, Setters, serialVersionUID, etc...
}
Voici une main()
méthode approximative qui persiste trois de ces entités:
public static void main(String[] args) {
EntityManager em = ... // from EntityManagerFactory, injection, etc.
em.getTransaction().begin();
A parent = new A();
A son = new A();
A daughter = new A();
son.setParent(parent);
daughter.setParent(parent);
parent.setChildren(Arrays.asList(son, daughter));
em.persist(parent);
em.persist(son);
em.persist(daughter);
em.getTransaction().commit();
}
Dans ce cas, les trois instances d'entité doivent être persistantes avant la validation de la transaction. Si je ne parviens pas à conserver l'une des entités dans le graphique des relations parent-enfant, une exception est lancée commit()
. Sur Eclipselink, c'est un RollbackException
détail de l'incohérence.
Ce comportement est configurable par l' cascade
attribut A
« s @OneToMany
et @ManyToOne
annotations. Par exemple, si je mets cascade=CascadeType.ALL
sur ces deux annotations, je pourrais persister en toute sécurité l'une des entités et ignorer les autres. Disons que j'ai persisté parent
dans ma transaction. L'implémentation JPA traverse parent
la children
propriété de car elle est marquée par CascadeType.ALL
. L'implémentation JPA trouve son
et daughter
là. Il persiste alors les deux enfants en mon nom, même si je ne l'ai pas explicitement demandé.
Encore une note. Il est toujours de la responsabilité du programmeur de mettre à jour les deux côtés d'une relation bidirectionnelle. En d'autres termes, chaque fois que j'ajoute un enfant à un parent, je dois mettre à jour la propriété parent de l'enfant en conséquence. La mise à jour d'un seul côté d'une relation bidirectionnelle est une erreur sous JPA. Mettez toujours à jour les deux côtés de la relation. Ceci est écrit sans ambiguïté à la page 42 de la spécification JPA 2.0:
Notez que c'est l'application qui a la responsabilité de maintenir la cohérence des relations d'exécution - par exemple, pour s'assurer que les côtés «un» et «plusieurs» d'une relation bidirectionnelle sont cohérents l'un avec l'autre lorsque l'application met à jour la relation au moment de l'exécution. .
Pour moi, le truc était d'utiliser la relation plusieurs à plusieurs. Supposons que votre entité A soit une division qui peut avoir des sous-divisions. Ensuite (en sautant les détails non pertinents):
Comme j'avais une logique métier étendue autour de la structure hiérarchique et que JPA (basé sur un modèle relationnel) est très faible pour le prendre en charge, j'ai introduit l'interface
IHierarchyElement
et l'écouteur d'entitéHierarchyListener
:la source
Top
s'agit d'une relation. Page 93 de la spécification JPA 2.0, Entity Listeners et méthodes de rappel: "En général, la méthode de cycle de vie d'une application portable ne doit pas appeler les opérations EntityManager ou Query, accéder à d'autres instances d'entité ou modifier les relations". Droite? Faites-moi savoir si je pars.