Quelle est la différence entre CascadeType.REMOVE et orphanRemoval dans JPA?

101

Quelle est la différence entre

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

et

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

Cet exemple est tiré du didacticiel Java EE, mais je ne comprends toujours pas les détails.

rand0m86
la source
La suppression des orphelins signifie que les entités dépendantes sont supprimées lorsque la relation avec leur entité «parente» est détruite.
Rahul Tripathi
1
Rédaction d'un cas de test qui pourrait illustrer le concept.
Martin Andersson

Réponses:

153

D' ici : -

Suppression en cascade

Marquer un champ de référence avec CascadeType.REMOVE (ou CascadeType.ALL, qui inclut REMOVE) indique que les opérations de suppression doivent être automatiquement mises en cascade sur les objets d'entité référencés par ce champ (plusieurs objets d'entité peuvent être référencés par un champ de collection):

@Entity
class Employee {
     :
    @OneToOne(cascade=CascadeType.REMOVE)
    private Address address;
     :
}

Suppression des orphelins

JPA 2 prend en charge un mode de suppression en cascade supplémentaire et plus agressif qui peut être spécifié à l'aide de l'élément orphanRemoval des annotations @OneToOne et @OneToMany:

@Entity
class Employee {
     :
    @OneToOne(orphanRemoval=true)
    private Address address;
     :
}

DIFFÉRENCE:-

La différence entre les deux paramètres réside dans la réponse à la déconnexion d'une relation. Par exemple, comme lors de la définition du champ d'adresse sur null ou sur un autre objet Address.

  • Si orphanRemoval = true est spécifié, l'instance Address déconnectée est automatiquement supprimée. Ceci est utile pour nettoyer les objets dépendants (par exemple, Adresse) qui ne devraient pas exister sans une référence d'un objet propriétaire (par exemple, Employé).
  • 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 .
Rahul Tripathi
la source
87

Un moyen simple de comprendre la différence entre CascadeType.REMOVEet orphanRemoval=true.

Pour la suppression des orphelins: si vous appelez setOrders(null), les Orderentités associées seront supprimées automatiquement dans la base de données.

Pour supprimer la cascade: si vous appelez setOrders(null), les Orderentités associées ne seront PAS supprimées automatiquement dans la base de données.

étude
la source
2
supprimer === supprimer
Abdull
9

Supposons que nous ayons une entité enfant et une entité parent. Un parent peut avoir plusieurs enfants.

@Entity
class parent {
  //id and other fields
 @OneToMany (orphanRemoval = "true",cascade = CascadeType.REMOVE)
   Set<Person> myChildern;
}

OrphanRemoval est un concept ORM, il indique si l'enfant est orphelin. il doit également être supprimé de la base de données.

Un enfant est orphelin lorsqu'il n'est pas accessible depuis son parent. Par exemple, si nous supprimons l'ensemble d'objets Personne (en le définissant sur un ensemble vide) ou le remplaçons par un nouvel ensemble, le parent ne peut plus accéder aux enfants de l'ancien ensemble et les enfants sont orphelins, donc les enfants sont condamnés à être supprimé dans la base de données également.

CascadeType.REMOVE est un concept au niveau de la base de données et il indique si le parent est supprimé, tous ses enregistrements associés dans la table enfant doivent être supprimés.

Monsieur Q
la source
2

Pratiquement, la différence réside dans le fait que vous essayez de mettre à jour les données (PATCH) ou de remplacer entièrement les données (PUT)

Supposons que vous supprimiez le customerque l'utilisation cascade=REMOVEsupprimera également les commandes des clients qui semblent intentionnelles et utiles.

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

Maintenant, disons que vous mettez à jour un customeravec orphanRemoval="true"cela supprimera toutes les commandes précédentes et les remplacera par celle fournie. ( PUTen termes de REST API)

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

Sans les orphanRemovalanciennes commandes seraient conservées. ( PATCHen termes de REST API)

garg10may
la source
1

Cette question étant très courante, cette réponse est basée sur cet article que j'ai écrit sur mon blog.

CascadeType.REMOVE

La CascadeType.REMOVEstratégie, que vous pouvez configurer explicitement:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.REMOVE
)
private List<PostComment> comments = new ArrayList<>();

ou l'hériter implicitement de la CascadeType.ALLstratégie:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL
)
private List<PostComment> comments = new ArrayList<>();

vous permet de propager l' removeopération de l'entité parente vers ses entités enfants.

Donc, si nous récupérons l' Postentité parente avec sa commentscollection et supprimons l' postentité:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments
    where p.id = :id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

entityManager.remove(post);

Hibernate va exécuter trois instructions de suppression:

DELETE FROM post_comment 
WHERE id = 2

DELETE FROM post_comment 
WHERE id = 3

DELETE FROM post 
WHERE id = 1

Les PostCommententités enfants ont été supprimées en raison de la CascadeType.REMOVEstratégie, qui a agi comme si nous supprimions également les entités enfants.

La stratégie d'élimination des orphelins

La stratégie de suppression des orphelins, qui doit être définie via l' orphanRemovalattribut:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

vous permet de supprimer la ligne de la table enfant lors de la suppression de l'entité enfant de la collection.

Donc, si nous chargeons l' Postentité avec sa commentscollection et supprimons la première PostCommentde la commentscollection:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments c
    where p.id = :id
    order by p.id, c.id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

post.remove(post.getComments().get(0));

Hibernate va exécuter une instruction DELETE pour la post_commentligne de table associée :

DELETE FROM post_comment 
WHERE id = 2

Pour plus de détails sur ce sujet, consultez également cet article .

Vlad Mihalcea
la source