Il est assez pénible de sauvegarder, supprimer, ou réattacher des objets un par un, surtout si vous traitez un graphe d'objets associés. Un cas habituel est une relation parent/enfant. Considérez l'exemple suivant :
Si les enfants de la relation parent/enfant étaient des types de valeur (par exemple, une collection d'adresses ou de chaînes de caractères), leur cycle de vie dépendraient du parent et aucune action ne serait requise pour "cascader" facilement les changements d'état. Si le parent est sauvegardé, les objets enfants de type de valeur sont sauvegardés également, si le parent est supprimé, les enfants sont supprimés, etc. Ceci fonctionne même pour des opérations telles que la suppression d'un enfant de la collection ; Hibernate détectera cela et, puisque les objets de type de valeur ne peuvent pas avoir des références partagées, supprimera l'enfant de la base de données.
Maintenant considérez le même scénario avec un parent et dont les objets enfants sont des entités, et non des types de valeur (par exemple, des catégories et des objets, ou un parent et des chatons). Les entités ont leur propre cycle de vie, supportent les références partagées (donc supprimer une entité de la collection ne signifie pas qu'elle peut être supprimée), et il n'y a par défaut pas de cascade d'état d'une entité vers n'importe quelle entité associée. Hibernate n'implémente pas la persistance par accessibilité par défaut.
Pour chaque opération basique de la session d'Hibernate - incluant persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()
- il y a un style de cascade correspondant. Respectivement, les styles de cascade s'appellent persist, merge, save-update, delete, lock, refresh, evict, replicate
. Si vous voulez qu'une opération soit cascadée le long d'une association, vous devez l'indiquer dans le document de mapping.
Par exemple :
<one-to-one name="person" cascade="persist"/>
Les styles de cascade peuvent être combinés :
<one-to-one name="person" cascade="persist,delete,lock"/>
Vous pouvez même utiliser cascade="all"
pour spécifier que toutes les opérations devraient être cascadées le long de l'association. La valeur par défaut cascade="none"
spécifie qu'aucune opération ne sera cascadée.
Une style de cascade spécial, delete-orphan
, s'applique seulement aux associations un-vers-plusieurs, et indique que l'opération delete()
devrait être appliquée à n'importe quel enfant qui est supprimé de l'association.
Recommandations :
Cela n'a généralement aucun sens d'activer la cascade sur une association <many-to-one>
ou <many-to-many>
. Les cascades sont souvent utiles pour des associations <one-to-one>
et <one-to-many>
.
Si la durée de vie de l'objet enfant est liée à la durée de vie de l'objet parent, faites en un objet du cycle de vie en spécifiant cascade="all,delete-orphan"
.
Sinon, vous pourriez ne pas avoir besoin de cascade du tout. Mais si vous pensez que vous travaillerez souvent avec le parent
et les enfants ensemble dans la même transaction, et que vous voulez vous éviter quelques frappes, considérez l'utilisation
de cascade="persist,merge,save-update"
.
Mapper une association (soit une simple association valuée, soit une collection) avec cascade="all"
marque l'association comme une relation de style parent/enfant où la sauvegarde/mise à jour/suppression du parent entraîne la sauvegarde/mise à jour/suppression de l'enfant ou des enfants.
En outre, une simple référence à un enfant d'un parent persistant aura pour conséquence la sauvegarde/mise à jour de l'enfant.
Cette métaphore est cependant incomplète. Un enfant qui devient non référencé par son parent n'est pas automatiquement supprimée, excepté dans le cas d'une association <one-to-many>
mappée avec cascade="delete-orphan"
. La sémantique précise des opérations de cascade pour une relation parent/enfant est la suivante :
Si un parent est passé à persist()
, tous les enfant sont passés à persist()
Si un parent est passé à merge()
, tous les enfants sont passés à merge()
Si un parent est passé à save()
, update()
ou saveOrUpdate()
, tous les enfants sont passés à saveOrUpdate()
Si un enfant détaché ou éphémère devient référencé par un parent persistant, il est passé à saveOrUpdate()
Si un parent est supprimé, tous les enfants sont passés à delete()
Si un enfant est déréférencé par un parent persistant, rien de spécial n'arrive - l'application devrait explicitement supprimer l'enfant si nécessaire - à moins que cascade="delete-orphan"
soit paramétré, au quel cas l'enfant "orphelin" est supprimé.
Enfin, la cascade des opérations peut être effectuée sur un graphe donné lors de l'appel de l'opération or lors du flush suivant. Toutes les opérations, lorsque cascadées, le sont sur toutes les entités associées atteignables lorsque l'opétation
est exécutée. Cependant save-upate
et delete-orphan
sont cascadées à toutes les entités associées atteignables lors du flush de la Session
.