Je suis un peu confus à propos de l'attribut JPA 2.0 orphanRemoval
.
Je pense comprendre que c'est nécessaire lorsque j'utilise les outils de génération de base de données de mon fournisseur JPA pour créer la base de données DDL sous-jacente afin de disposer d'un ON DELETE CASCADE
sur la relation particulière.
Toutefois, si le DB existe et qu'il a déjà un ON DELETE CASCADE
sur la relation, cela ne suffit-il pas pour castrer la suppression en cascade de manière appropriée? Que fait la orphanRemoval
en plus?
À votre santé
orphanRemoval
n'a rien à voir avec ON DELETE CASCADE
.
orphanRemoval
est un tout chose spécifique à l'ORM. Il marque que l'entité "enfant" doit être supprimée lorsqu'elle n'est plus référencée à partir de l'entité "parent", par exemple. lorsque vous supprimez l'entité enfant de la collection correspondante de l'entité parent.
ON DELETE CASCADE
est un élément spécifique à la base de données, il supprime la ligne "enfant" de la base de données lorsque la ligne "parent" est supprimée.
Un exemple de formulaire pris ici :
Lorsqu'un objet entité Employee
est supprimé, l'opération de suppression est effectuée en cascade sur l'objet entité référencé Address
. À cet égard, orphanRemoval=true
et cascade=CascadeType.REMOVE
sont identiques et si orphanRemoval=true
est spécifié, CascadeType.REMOVE
est redondant.
La différence entre les deux paramètres réside dans la réponse à la déconnexion d'une relation. Par exemple, lorsque vous définissez le champ d'adresse sur null
ou sur un autre objet Address
.
Si orphanRemoval=true
est spécifié, l'instance déconnectée Address
est automatiquement supprimée. Ceci est utile pour nettoyer des objets dépendants (par exemple Address
) qui ne devraient pas exister sans une référence provenant d'un objet propriétaire (par exemple Employee
).
Si seul cascade=CascadeType.REMOVE
est spécifié, aucune action automatique n'est entreprise car la déconnexion d'une relation n'est pas une opération de suppression.
Pour éviter les suspensions de références résultant de la suppression d'Orphan, cette fonctionnalité ne doit être activée que pour les champs contenant des objets dépendants privés non partagés.
J'espère que cela rend les choses plus claires.
Dès que vous supprimez une entité enfant de la collection, vous supprimez également cette entité enfant de la base de données. orphanRemoval implique également que vous ne pouvez pas changer de parents; s'il y a un département qui a des employés, une fois que vous enlevez cet employé pour le placer dans un autre département, vous aurez par inadvertance supprimé cet employé de la base de données au moment de la mise à jour/du commit (selon la première éventualité). Le moral consiste à définir orphanRemoval sur true tant que vous êtes certain que les enfants de ce parent ne migreront pas vers un autre parent tout au long de leur existence. L'activation de orphanRemoval ajoute automatiquement REMOVE à la liste en cascade.
Le mappage JPA équivalent pour le DDL ON DELETE CASCADE
est cascade=CascadeType.REMOVE
. La suppression orpheline signifie que les entités dépendantes sont supprimées lorsque la relation avec leur entité "parent" est détruite. Par exemple, si un enfant est supprimé d'une relation @OneToMany
sans le supprimer explicitement dans le gestionnaire d'entités.
La réponse de @GaryK est absolument géniale, j'ai passé une heure à chercher une explication orphanRemoval = true
contre CascadeType.REMOVE
et cela m'a aidé à comprendre.
En résumé: orphanRemoval = true
fonctionne de la même manière que CascadeType.REMOVE
SEULEMENT SI nous supprimons un objet (entityManager.delete(object)
) et nous souhaitons également supprimer les objets enfants.
Dans des situations complètement différentes, lorsque nous récupérons des données telles que List<Child> childs = object.getChilds()
, puis retirons un enfant (entityManager.remove(childs.get(0)
) à l'aide de orphanRemoval=true
, l'entité correspondant à childs.get(0)
sera supprimée de la base de données. .
La suppression d’orphelins a le même effet que ON DELETE CASCADE dans le scénario suivant: - Disons que nous avons une relation simple, plusieurs à un, entre une entité d’étudiant et une entité de guide, dans laquelle de nombreux étudiants peuvent être mappés vers le même guide. relation de clé étrangère entre Student et Table Guide telle que la table student porte id_guide comme FK.
@Entity
@Table(name = "student", catalog = "helloworld")
public class Student implements Java.io.Serializable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id")
private Integer id;
@ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@JoinColumn(name = "id_guide")
private Guide guide;
// l'entité mère
@Entity
@Table(name = "guide", catalog = "helloworld")
public class Guide implements Java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 9017118664546491038L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@Column(name = "name", length = 45)
private String name;
@Column(name = "salary", length = 45)
private String salary;
@OneToMany(mappedBy = "guide", orphanRemoval=true)
private Set<Student> students = new HashSet<Student>(0);
Dans ce scénario, la relation est telle que l'entité étudiante est le propriétaire de la relation et, en tant que telle, nous devons sauvegarder l'entité étudiante afin de conserver le graphique d'objet entier, par exemple.
Guide guide = new Guide("John", "$1500");
Student s1 = new Student(guide, "Roy","ECE");
Student s2 = new Student(guide, "Nick", "ECE");
em.persist(s1);
em.persist(s2);
Nous mappons ici le même guide avec deux objets étudiant différents et, étant donné que CASCADE.PERSIST est utilisé, le graphe d'objet sera enregistré comme suit dans la table de la base de données (MySql dans mon cas).
Table des étudiants: -
1 Roy ECE 1
2 Nick ECE 1
1 Jean 1500 $
et maintenant, si je veux supprimer un des étudiants, en utilisant
Student student1 = em.find(Student.class,1);
em.remove(student1);
et quand un dossier d’étudiant est supprimé, le dossier de guide correspondant doit également être supprimé, c’est là que l’attribut CASCADE.REMOVE de l’entité Étudiant entre en image et permet de supprimer l’élève avec l’identificateur 1 ainsi que l’objet guide correspondant (identificateur). 1). Mais dans cet exemple, un autre objet étudiant est mappé sur le même enregistrement de guide et, à moins d'utiliser l'attribut orphanRemoval = true dans l'entité de guide, le code de suppression ci-dessus ne fonctionnera pas.
La différence est:
- orphanRemoval = true: l'entité "enfant" est supprimée lorsqu'elle n'est plus référencée (son parent ne peut pas être supprimé).
- CascadeType.REMOVE: l'entité "enfant" est supprimée uniquement lorsque son "parent" est supprimé.