17mar. 2011

NHibernate/Hibernate : Gérer la suppression dans une table parent

Etre abandonné par ses parents, c'est jamais très drôle. Surtout si une clé étrangère vous y relie. Votre base de données crie au scandale et vous rapelle qu'il y a des règles en ce bas monde ; enfin des contraintes exactement.

Voyons comment gérer la suppression d'une ligne référencée par une contrainte clé étrangère avec NHibernate. En effet dans certains cas, on peut-être amené à supprimer la ligne parente et vouloir conserver la ligne enfant.

La base de données comme l'ORM doivent être configuré de manière à implémenter ce comportement.

Etape 1 : Spécifier la étrangère

Afin de pouvoir briser le lien parent-enfant il faut spécifier au SGBD - dans l'exemple SQL Server 2008 - la conduite à adopter en cas de suppression de le la ligne parente. Cela se fait lors au niveau de la clé étrangère :

ALTER TABLE MacReibenSchema.MyChildTable  
ADD CONSTRAINT FK_MyChildTable_MyParentTable
      FOREIGN KEY(ParentId) 
      REFERENCES MacReibenSchema.MyParentTable (Id)
         ON UPDATE SET NULL

Ici, on ajoute une clé étrangère sur la table enfant et on spécifie par la clause ON UPDATE suivi d'une valeur pour le paramêtre de cascading qui est "SET NULL".

SQL Server placera alors automatiquement la clé étrangère à NULL lors de la suppression de la ligne référencée dans la page parente. Pas de suppression, on garde l'enregistrement enfant.

Il convient bient sûr d'autoriser la valeur NULL dans la colonne clé étrangère dans la table enfant. Sans quoi le SGBD renverra une erreur d'intégrité.

Etape 2 : Configurer la gestion des contraintes sur Hibernate

Au niveau d'NHibernate/Hibernate pluisieurs options sont possibles pour gérer les collections enfants. L'attribut qui permet de gérer ces options est "cascade". Voici les comportements proposés :

  • delete – Si l'objet parent est supprimé, supprime les objets enfants.
  • delete-orphans – Si un objet est supprimé, supprime tous les objets qui lui sont associé. De la même manière, si un objet n'a plus de lien avec le modèle, le supprimer.
  • all-delete-orhpans - Si un objet est sauvé, supprimé ou mis à jour, verifie les objets associés, les sauve, les supprime ou les mets à jour. Si un objets n'a plus d'association avec le modèle, le supprime.
  • save-update – Lorsqu'un objet est sauvé ou mi à jour, mets à jour les objets associés qui sont alors flaggés dirty.
  • all – Si un object est sauve, supprimé ou mis à jour, vérifie les objets associés, les sauvent, les supprime ou les mets à jour.
  • none – Le développeur doit gérer manuellement le processus de mise à jour des objets lors de la mise à jour du modèle.

Nous voulons conserver la ligne enfant malgré la suppression de la ligne parente. Nous allons donc donner à l'attribut cascade la valeur save-update dans l'objet parent. Exemple :

<bag name="ChildCollection" cascade="save-update"
        inverse="true" lazy="false">
    <key>
      <column name="ParentId" not-null="false"/>
    </key>
  <one-to-many class="MacReiben.MyChildTable, MyApplication.Model" />
</bag>

On remarque la présence de l'attribut inverse qui a la valeur true. Cet attribut informe Hibernate que l'objet parent est propriétaire de la relation. C'est au niveau de l'objet parent que la stratégie de cascading sera appliquée.

De cette manière lors de la mise à jour, on conservera la ligne enfant et la clé étrangère dans la table enfant sera mise à jour avec la valeur NULL en cas de suppression.

L'attribut lazy est quant à lui placé à false car je n'ai pas besoin de générer de proxy pour charger mes données.

Bibliographie

aucun commentaire

Fil des commentaires de ce billet

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.