J'ai un problème avec un simple @OneToMany
mappage entre une entité parent et une entité enfant. Tout fonctionne bien, seuls les enregistrements enfants ne sont pas supprimés lorsque je les supprime de la collection.
Le parent:
@Entity
public class Parent {
@Id
@Column(name = "ID")
private Long id;
@OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
private Set<Child> childs = new HashSet<Child>();
...
}
L'enfant:
@Entity
public class Child {
@Id
@Column(name = "ID")
private Long id;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="PARENTID", nullable = false)
private Parent parent;
...
}
Si je supprime maintenant l'enfant du jeu d'enfants, il ne sera pas supprimé de la base de données. J'ai essayé d'annuler le child.parent
référence, mais cela n'a pas fonctionné non plus.
Les entités sont utilisées dans une application Web, la suppression s'effectue dans le cadre d'une requête Ajax. Je n'ai pas de liste d'enfants supprimés lorsque le bouton de sauvegarde est enfoncé, je ne peux donc pas les supprimer implicitement.
Le comportement de JPA est correct (sens conformément à la spécification ): les objets ne sont pas supprimés simplement parce que vous les avez supprimés d'une collection OneToMany. Cela correspond à des extensions spécifiques au fournisseur, mais JPA natif ne les prend pas en charge.
C'est en partie parce que JPA ne sait pas réellement si elle doit supprimer quelque chose qui a été supprimé de la collection. En termes de modélisation d'objet, il s'agit de la différence entre composition et "agrégation *".
Dans composition , l'entité enfant n'a pas d'existence sans le parent. Un exemple classique est entre House et Room. Supprimer la maison et les chambres vont aussi.
L'agrégation est un type d'association plus modéré, caractérisé par Course et Student. Supprimez le cours et l'étudiant existe toujours (probablement dans d'autres cours).
Vous devez donc utiliser des extensions spécifiques au fournisseur pour forcer ce comportement (le cas échéant) ou supprimer explicitement l'enfant ET le supprimer de la collection du parent.
Je suis conscient de:
Outre la réponse de cletus, JPA 2. , définitif depuis décembre 2010, introduit un attribut orphanRemoval
sur @OneToMany
annotations. Pour plus de détails, voir ceci entrée de blog .
Notez que, puisque la spécification est relativement nouvelle, tous les fournisseurs JPA 1 ne disposent pas d’une implémentation finale de JPA 2. Par exemple, la version version d'Hibernate 3.5.0-Beta-2 ne prend pas encore en charge cet attribut.
Vous pouvez essayer ceci:
@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true)
.
Comme expliqué, il n'est pas possible de faire ce que je veux avec JPA. J'ai donc utilisé l'annotation hibernate.cascade. Avec cela, le code correspondant dans la classe Parent ressemble maintenant à ceci:
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy = "parent")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.DELETE,
org.hibernate.annotations.CascadeType.MERGE,
org.hibernate.annotations.CascadeType.PERSIST,
org.hibernate.annotations.CascadeType.DELETE_Orphan})
private Set<Child> childs = new HashSet<Child>();
Je ne pouvais pas simplement utiliser 'ALL' car cela aurait également supprimé le parent.
Ici, en cascade, dans le contexte de la suppression, signifie que les enfants sont supprimés si vous supprimez le parent. Pas l'association. Si vous utilisez Hibernate en tant que fournisseur JPA, vous pouvez le faire en utilisant cascade spécifique d'Hibernate .
Vous pouvez essayer ceci:
@OneToOne(cascade = CascadeType.REFRESH)
ou
@OneToMany(cascade = CascadeType.REFRESH)
@Entity
class Employee {
@OneToOne(orphanRemoval=true)
private Address address;
}
Voir ici .