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)?
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.
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
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.
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 . `
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 .
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.