web-dev-qa-db-fra.com

Quel est le sens de CascadeType.ALL pour une association JPA @ManyToOne

Je pense avoir mal compris la signification de la cascade dans le contexte d'une relation @ManyToOne.

L'affaire:

public class User {

   @OneToMany(fetch = FetchType.EAGER)
   protected Set<Address> userAddresses;

}

public class Address {

   @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
   protected User addressOwner;

}

Quelle est la signification du cascade = CascadeType.ALL? Par exemple, si je supprime une certaine adresse de la base de données, en quoi le fait que j'ai ajouté le cascade = CascadeType.ALL affecte-t-il mes données (la User, je suppose)?

159
forhas

CascadeType.ALL signifie que la persistance propagera (en cascade) toutes les opérations EntityManager (PERSIST, REMOVE, REFRESH, MERGE, DETACH) aux entités associées.

Dans votre cas, cela semble être une mauvaise idée, car la suppression d'une Address conduirait à la suppression de la User correspondante. Un utilisateur pouvant avoir plusieurs adresses, les autres adresses deviendraient orphelines. Toutefois, le cas inverse (annoter User) serait logique: si une adresse appartient à un seul utilisateur, il est prudent de propager la suppression de toutes les adresses appartenant à un utilisateur si cet utilisateur est supprimé. 

BTW: vous souhaiterez peut-être ajouter un attribut mappedBy="addressOwner" à votre User pour signaler au fournisseur de persistance que la colonne de jointure doit figurer dans la table ADDRESS.

279
kostja

Voir ici pour un exemple tiré de la documentation OpenJPA. CascadeType.ALL signifie qu'il fera toutes les actions.

Citation:

CascadeType.PERSIST: Lors de la persistance d'une entité, persistez également les entités contenues dans ce champ. Nous suggérons une application généralisée de cette règle de cascade, car si EntityManager trouve un champ qui référence une nouvelle entité lors du vidage et que le champ n'utilise pas CascadeType.PERSIST, il s'agit d'une erreur.

CascadeType.REMOVE: lors de la suppression d'une entité, supprimez également les entités contenues dans ce champ.

CascadeType.REFRESH: lors de l'actualisation d'une entité, actualisez également les entités contenues dans ce champ.

CascadeType.MERGE: lors de la fusion d'un état d'entité, fusionne également les entités contenues dans ce champ.

Sebastian

39
seba.wagner

D'après la spécification EJB3.0 :

L'utilisation de l'élément d'annotation en cascade peut être utilisée pour propager le effet d'une opération sur les entités associées. La cascade la fonctionnalité est le plus souvent utilisée dans les relations parent-enfant.

Si X est une entité gérée, l'opération de suppression entraîne sa transformation en enlevé. L'opération remove est en cascade vers les entités référencées par X, si les relations de X avec ces autres entités sont annotées avec la cascade = REMOVE ou cascade = ALL la valeur de l'élément d'annotation.

En résumé, les relations d'entité définies avec CascadeType.All garantissent que tous les événements de persistance tels que persister, actualiser, fusionner et supprimer qui se produisent sur le parent sont transmis à l'enfant. La définition d'autres options CascadeType fournit au développeur un niveau de contrôle plus granulaire sur la manière dont l'association d'entités gère la persistance.

Par exemple, si j'avais un objet Book contenant une liste de pages et que j'ajoute un objet de page dans cette liste. Si l'annotation @OneToMany définissant l'association entre le livre et la page est marquée comme CascadeType.All, la persistance du livre entraînerait également la persistance de la page dans la base de données.

17
Kevin Bowersox

Comme je l'ai expliqué dans cet article et dans mon livre, Persistance Java hautes performances , vous ne devez jamais utiliser CascadeType.ALL sur @ManyToOne car les transitions d'état des entités doivent être propagées des entités parent aux entités enfants.

Le côté @ManyToOne est toujours l'association Child car il doit mapper le FK sous-jacent.

Par conséquent, déplacez le CascadeType.ALL de l'association @ManyToOne vers le @OneToMany qui devrait utiliser l'attribut mappedBy puisqu'il s'agit de le mappage un à plusieurs le plus efficace . `

13
Vlad Mihalcea

Dans JPA 2.0, si vous souhaitez supprimer une adresse si vous l'avez supprimée d'une entité utilisateur, vous pouvez ajouter orphanRemoval=true (au lieu de CascadeType.REMOVE) à votre @OneToMany.

Plus d'explications entre orphanRemoval=true et CascadeType.REMOVE est ici .

9
Emilien Brigand

Si vous souhaitez simplement supprimer l'adresse attribuée à l'utilisateur et ne pas affecter la classe d'entité User, vous devez essayer quelque chose comme ça:

@Entity
public class User {
   @OneToMany(mappedBy = "adressOwner", cascade = CascadeType.ALL)
   protected Set<Address> userAddresses = new HashSet<>();
}

@Entity 
public class Adresses {
   @ManyToOne(cascade = CascadeType.REFRESH) @JoinColumn(name = "user_id")
   protected User adressOwner;
}

De cette façon, vous n'avez pas à vous soucier de l'utilisation de fetch dans les annotations. Mais rappelez-vous qu'en supprimant l'utilisateur, vous supprimerez également l'adresse connectée à l'objet utilisateur.

0
szachMati