NHibernate et les propriétés magiques

Posted by – March 14, 2010

Préambule

La différence entre la théorie et la pratique ? en théorie, il n’y en a pas, en pratique, si.

Le monde du développement est loin d’être un monde idéal, dans la réalité, nous rencontrons souvent des situations particulières auxquelles il faut faire face. Un framework, en plus de répondre aux besoins les plus généraux, se doit également de fournir des solutions de contournements, des “astuces de programmation” diront certains.

Dans ce court billet, nous allons parler de 2 fonctionnalités de NHibernate sur les propriétés lorsque notre modèle ou le contexte ne répond pas exactement à l’idéal que l’on devrait avoir en termes d’architecture.

Imaginons que l’on ait une entité Individu, que l’on affichera sur une page, sous forme d’une liste d’individus. Dans notre modèle nous avons la relation simple suivante entre un individu et son appartenance à un établissement :

La liste paginée est triable (cf. ce billet pour la pagination et le tri en NHibernate) par le nom, prénom, le nom de l’établissement. On pourrait facilement créer des entités Individu et Etablissement avec cette relation, mais l’on ne souhaite pas le faire, car nous avons uniquement besoin du nom de l’établissement pour le tri, sans vouloir représenter tout le modèle derrière Etablissement. Le tri et la pagination ne peuvent s’effectuer que par l’intermédiaire de NHibernate, nous avons donc besoin d’une proprété mappée dans une classe pour que l’ORM puisse l’atteindre et actionner le tri.

Formula

les formula permettent ce genre d’astuces de programmation : avoir une propriété EtabNom qui pointe vers une table externe (et non vers une classe comme on le ferait habituellement avec du many-to-one) mais sous forme d’une requête SQL, la propriété se tranforme alors en :

<property    name="EtabNom"   formula="(select etab.NOMALPHA from ETABLISSEMENT etab where etab.ID=ETAB_ID)"> </property>

ce qui donne pour l’entité Individu, où ETAB_ID est un champ de la table INDIVIDU mappée :

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" >   <class name="MyNamespace.Individu, MyAssembly" table="INDIVIDU">     <id name="ID" column="ID" type="System.Int32" access="property">       <generator class="identity"></generator>     </id>     <property name="Nom" column="nom" not-null="true" type="System.String" access="property"/>     <property name="Prenom" column="prenom" not-null="true" type="System.String" access="property"/>     <property name="Email" column="email" not-null="true" type="System.String" access="property"/>     <property name="EtabNom"               formula="(select etab.NOMALPHA from ETABLISSEMENT etab where etab.ID=ETAB_ID)"></property>   </class> </hibernate-mapping>

on pourra alors trier sur la propriété individu.EtabNom sans utiliser d’entité Etablissement. Une requête Linq NHibernate du type

lasession.Linq<Individu>().OrderBy(ind => ind.EtabNom).Take(20);

se traduira en SQL de la façon suivante, on retrouve le morceau SQL de l’attribut formula :

SELECT   top 20 individu2_.ID            AS IND1_3_0_,                 individu2_.nom           AS ind4_3_0_,                 individu2_.prenom        AS ind5_3_0_,                 individu2_.email         AS ind7_3_0_,                 individu2_.etab_id       AS etab12_3_0_,                 (SELECT etab.NOMALPHA                  FROM   ETABLISSEMENT etab                  WHERE  etab.ID = individu2_.ETAB_ID) AS formula0_0_ FROM    INDIVIDU individu2_ ORDER BY (SELECT etab.NOMALPHA           FROM   ETABLISSEMENT etab           WHERE  etab.ID = individu2_.ETAB_ID) ASC

ce type de propriété peut être également utile en tant que champ calculé, un count sur un champ d’une table externe sans pour autant avoir une collection mappée. Bien entendu, les formula sont à utiliser avec parcimonie, étant donné que c’est du pur SQL, à utiliser sur des tables de références.

Noop

A l’inverse, on pourrait avoir une classe Etablissement qui ne contiendrait pas de propriété Individus (dans le sens POCO donc non mappée) mais qui serait utile d’avoir pour effectuer des sélections : donne-moi les établissements qui ont plus de 200 individus intérimaires. Au niveau de la collection, cela rejoint ce billet qui parlait de ce type de set.

Le type d’accès noop (ou none) sur une propriété permet cela : pouvoir effectuer une requête sur une propriété non mappé dans une classe.

Par exemple, dans le .hbm, on pourrait avoir une collection one-to-many qui nous donne les intérimaires d’un établissement, les individus intérimaires seraient de type 3, une collection qui se traduit par :

<bag  name="Interim" access="noop"            where="(typind = 3)">      <key column ="ETAB_ID"/>      <one-to-many class="MyNamespace.Individu, MyAssembly"/>  </bag>

dans la classe Etablissement, la propriété Interim n’existe pas.

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" >   <class name="MyNamespace.Etablissement, MyAssembly" table="ETABLISSEMENT">     <id name="ID" column="ID" type="System.Int32" access="property">       <generator class="identity"></generator>     </id>     <property name="EtabNom" column="NOMALPHA" not-null="true" type="System.String" access="property"/>     <bag name="Interim"          where="(typind = 3)"          access="noop">       <key column ="ETAB_ID"/>       <one-to-many class="MyNamespace.Individu, MyAssembly"/>     </bag>   </class> </hibernate-mapping>

cela permet ensuite d’effectuer des requêtes HQL, les établissements ayant plus de 200 intérimaires :

var etabs = session.CreateQuery("   from Etablissement etab   where size(etab.Interim) > 200").List<Etablissement>();

qui se traduit en la requête SQL :

SELECT etablissem0_.ID       AS ETABCONS1_4_,           etablissem0_.NOMALPHA AS ETABCONS2_4_    FROM   ETABLISSEMENT etablissem0_    WHERE  (SELECT count(interim1_.ETAB_ID)            FROM INDIVIDU interim1_            WHERE etablissem0_.ID = interim1_.ETAB_ID            AND ((interim1_.typind = 3))) > 200

de façon identique, à utiliser avec prudence, sous peine de rompre l’abstraction que nous avons avec le SQL.

1 Comment on NHibernate et les propriétés magiques

  1. marchal.jb says:

    Merci bcp pour l’info, je ne connaissais pas cette possibilité. ( On a pas toujours le temps de se plonger à fond dans la documentation… )

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>