hibernate-orm/reference/fr/modules/query_sql.xml

751 lines
31 KiB
XML
Raw Normal View History

<?xml version="1.0" encoding="ISO-8859-1"?>
<chapter id="querysql" revision="2">
<title>SQL natif</title>
<para>
Vous pouvez aussi <20>crire vos requ<71>tes dans le dialecte SQL natif de votre base de donn<6E>es.
Ceci est utile si vous souhaitez utiliser les fonctionnalit<69>s sp<73>cifiques de votre base de
donn<6E>es comme le mot cl<63> <literal>CONNECT</literal> d'Oracle. Cette fonctionnalit<69> offre par ailleurs un moyen
de migration plus propre et doux d'une application bas<61>e sur SQL/JDBC vers
une application Hibernate.
</para>
<para>Hibernate3 vous permet de sp<73>cifier du SQL <20>crit <20> la main (incluant les proc<6F>dures stock<63>es)
pour toutes les op<6F>rations de cr<63>ation, mise <20> jour, suppression et chargement.</para>
<sect1 id="querysql-creating" revision="4">
<title>Utiliser une <literal>SQLQuery</literal></title>
<para>L'ex<65>cution des requ<71>tes en SQL natif est contr<74>l<EFBFBD>e par l'interface <literal>SQLQuery</literal>,
laquelle est obtenue en appelant <literal>Session.createSQLQuery()</literal>.
Dans des cas extr<74>mement simples, nous pouvons utiliser la forme suivante :
</para>
<sect2>
<title>Requ<EFBFBD>tes scalaires</title>
<para>La requ<71>te SQL la plus basique permet de r<>cup<75>rer une liste de (valeurs) scalaires.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
]]></programlisting>
<para>Ces deux requ<71>tes retourneront un tableau d'objets (Object[]) avec
les valeurs scalaires de chacune des colonnes de la table CATS.
Hibernate utilisera le ResultSetMetadata pour d<>duire l'ordre et le type
des valeurs scalaires retourn<72>es.</para>
<para>Pour <20>viter l'overhead li<6C> <20> <literal>ResultSetMetadata</literal> ou simplement pour
<20>tre plus explicite dans ce qui est retourn<72>, vous pouvez utiliser <literal>addScalar()</literal>.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
]]></programlisting>
<para>Cette requ<71>te sp<73>cifie:</para>
<itemizedlist>
<listitem>
<para>la cha<68>ne de caract<63>re SQL</para>
</listitem>
<listitem>
<para>les colonnes et les types retourn<72>s</para>
</listitem>
</itemizedlist>
<para>Cela retournera toujours un tableau d'objets, mais sans utiliser le
<literal>ResultSetMetdata</literal>, mais r<>cup<75>rera explicitement les colonnes
ID, NAME and BIRTHDATE column <20>tant de respectivement de type Long, String et Short,
depuis le resultset sous jacent. Cela signifie aussi que seules ces colonnes seront
retourn<72>es m<>me si la requ<71>te utilise <literal>*</literal>
et aurait pu retourner plus que les trois colonnes list<73>es.</para>
<para>Il est possible de ne pas d<>finir l'information sur le type pour toutes ou partie
des calaires.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME")
.addScalar("BIRTHDATE")
]]></programlisting>
<para>Il s'agit essentiellement de la m<>me requ<71>te que pr<70>c<EFBFBD>demment, mais
le <literal>ResultSetMetaData</literal> est utilis<69> pour d<>cider des types de NAME
et BIRTHDATE alors que le type de ID est explicitement sp<73>cifi<66>.</para>
<para>Les java.sql.Types retourn<72>s par le ResultSetMetaData sont mapp<70>s aux type Hibernate
via le Dialect. Si un type sp<73>cifique n'est pas mapp<70> ou est mapp<70> <20> un type non souhait<69>, il
est possible de personnaliser en invoquant <literal>registerHibernateType</literal> dans
le Dialect.</para>
</sect2>
<sect2>
<title>Requ<EFBFBD>tes d'entit<69>s</title>
<para>Les requ<71>tes pr<70>c<EFBFBD>dentes ne retournaient que des valeurs scalaires,
retournant basiquement que les valeurs brutes du resultset. Ce qui suit montre
comment r<>cup<75>rer des entit<69>s depuis une requ<71>te native SQL, gr<67>ce <20>
<literal>addEntity()</literal>.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
]]></programlisting>
<para>Cette requ<71>te sp<73>cifie:</para>
<itemizedlist>
<listitem>
<para>La cha<68>ne de caract<63>re de requ<71>te SQL</para>
</listitem>
<listitem>
<para>L'entit<69> retourn<72>e par la requ<71>te</para>
</listitem>
</itemizedlist>
<para>Avec Cat mapp<70> comme classe avec les colonnes ID, NAME
et BIRTHDATE, les requ<71>tes pr<70>c<EFBFBD>dentes retournent toutes deux une liste
o<> chaque <20>l<EFBFBD>ment est une entit<69> Cat.</para>
<para>Si l'entit<69> est mapp<70>e avec un <literal>many-to-one</literal> vers
une autre entit<69>, il est requis de retourner aussi cette entit<69> en ex<65>cutant
la requ<71>te native, sinon une erreur "column not found" sp<73>cifique <20> la base de
donn<6E>es sera soulev<65>e. Les colonnes additionnelles seront automatiquement
retourn<72>es en utilisant la notation *, mais nous pr<70>f<EFBFBD>rons <20>tre explicites
comme dans l'exemple suivant avec le <literal>many-to-one</literal> vers
<literal>Dog</literal>:</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
]]></programlisting>
<para>Ceci permet <20> cat.getDog() de fonctionner normalement.</para>
</sect2>
<sect2>
<title>G<EFBFBD>rer les associations et collections</title>
<para>Il est possible de charger agressivement <literal>Dog</literal> pour
<20>viter le chargement de proxy qui signifie aller retour suppl<70>mentaire vers
la base de donn<6E>es. Ceci est faisable via la m<>thode <literal>addJoin()</literal>,
qui vous permet de joindre une association ou collection.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dog");
]]></programlisting>
<para>Dans cet exemple, les <literal>Cat</literal> retourn<72>s auront leur
propri<72>t<EFBFBD> <literal>dog</literal> enti<74>rement initialis<69>es sans aucun aller/retour
suppl<70>mentaire vers la base de donn<6E>es. Notez que nous avons ajout<75> un alias
("cat") pour <20>tre capable de sp<73>cifier la propri<72>t<EFBFBD> cible de la jointure.
Il est possible de faire la m<>me jointure aggressive pour les collections, e.g. si le
<literal>Cat</literal> a un one-to-many vers <literal>Dog</literal>.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");
]]></programlisting>
<p>Nous arrivons aux limites de ce qui est possible avec les requ<71>tes natives
sans les modifier pour les rendre utilisables par Hibernate; les probl<62>mes
surviennent lorsque nous essayons de retourner des entit<69>s du m<>me type ou
lorsque les alias/colonnes par d<>faut ne sont plus suffisants..</p>
</sect2>
<sect2>
<title>Retour d'entit<69>s multiples</title>
<para>Jusqu'<27> pr<70>sent, les colonnes du resultset sont suppos<6F>es <20>tre les m<>mes
que les colonnes sp<73>cifi<66>es dans les fichiers de mapping. Ceci peut
<20>tre probl<62>matique pour les requ<71>tes SQL qui effectuent de multiples
jointures vers diff<66>rentes tables, puisque les m<>mes colonnes peuvent
appara<72>tre dans plus d'une table.</para>
<para>L'injection d'alias de colonne est requis pour la requ<71>te suivante
(qui risque de ne pas fonctionner):</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
]]></programlisting>
<para>Le but de cette requ<71>te est de retourner deux instances de Cat par ligne,
un chat et sa m<>re. Cela <20>chouera puisqu'il y a conflit de nom puisqu'ils sont
mapp<70>s au m<>me nom de colonne et que sur certaines base de donn<6E>es, les alias
de colonnes retourn<72>s seront plut<75>t de la forme
"c.ID", "c.NAME", etc. qui ne sont pas <20>gaux aux colonnes sp<73>cifi<66>es dans les
mappings ("ID" and "NAME").</para>
<para>La forme suivante n'est pas vuln<6C>rable <20> la duplication des noms de colonnes:</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
]]></programlisting>
<para>Cette requ<71>te sp<73>cifie:</para>
<itemizedlist>
<listitem>
<para>la requ<71>te SQL, avec des r<>ceptacles pour qu'Hibernate injecte les alias de colonnes</para>
</listitem>
<listitem>
<para>les entit<69>s retourn<72>s par la requ<71>te</para>
</listitem>
</itemizedlist>
<para>Les notations {cat.*} et {mother.*} utilis<69>es sont un <20>quivalent <20> 'toutes les propri<72>t<EFBFBD>s'.
Alternativement, vous pouvez lister les colonnes explicitement, mais m<>me pour ce cas, nous
laissons Hibernate injecter les alias de colonne pour chaque propri<72>t<EFBFBD>.
Le r<>ceptable pour un alias de colonne est simplement le nom de la propri<72>t<EFBFBD>
qualifi<66> par l'alias de la table. Dans l'exemple suivant, nous r<>cup<75>rons
les chats et leur m<>re depuis une table diff<66>rentes (cat_log) de celle d<>clar<61>e
dans les mappings. Notez que nous pouvons aussi utiliser les alias de propri<72>t<EFBFBD>
dans la clause where si nous le voulons.</para>
<programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
]]></programlisting>
<sect3 id="querysql-aliasreferences" revision="2">
<title>R<EFBFBD>f<EFBFBD>rences d'alias et de propri<72>t<EFBFBD></title>
<para>Pour la plupart des cas pr<70>c<EFBFBD>dents, l'injection d'alias est requis,
mais pour les requ<71>tes relatives <20> des mappings plus complexes, comme
les propri<72>t<EFBFBD>s composite, les discriminants d'h<>ritage, les collections etc., il
y a des alias sp<73>cifiques <20> utiliser pour permettre <20> Hibernate l'injection
des bons alias.</para>
<para>Le tableau suivant montre les diverses possiblit<69>s d'utilisation
d'injection d'alias. Note: les noms d'alias dans le r<>sultat sont des
exemples, chaque alias aura un nom unique et probablement diff<66>rent lorsqu'ils
seront utilis<69>s.</para>
<table frame="topbot" id="aliasinjection-summary">
<title>Nom d'injection d'alias</title>
<tgroup cols="3">
<colspec colwidth="1*" />
<colspec colwidth="1*" />
<colspec colwidth="2.5*" />
<thead>
<row>
<entry>Description</entry>
<entry>Syntaxe</entry>
<entry>Exemple</entry>
</row>
</thead>
<tbody>
<row>
<entry>Une propri<72>t<EFBFBD> simple</entry>
<entry><literal>{[aliasname].[propertyname]</literal></entry>
<entry><literal>A_NAME as {item.name}</literal></entry>
</row>
<row>
<entry>Une propri<72>t<EFBFBD> composite</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
<entry><literal>CURRENCY as {item.amount.currency}, VALUE as
{item.amount.value}</literal></entry>
</row>
<row>
<entry>Discriminateur d'une entit<69></entry>
<entry><literal>{[aliasname].class}</literal></entry>
<entry><literal>DISC as {item.class}</literal></entry>
</row>
<row>
<entry>Toutes les propri<72>t<EFBFBD>s d'une entit<69></entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{item.*}</literal></entry>
</row>
<row>
<entry>La cl<63> d'une collection</entry>
<entry><literal>{[aliasname].key}</literal></entry>
<entry><literal>ORGID as {coll.key}</literal></entry>
</row>
<row>
<entry>L'id d'une collection</entry>
<entry><literal>{[aliasname].id}</literal></entry>
<entry><literal>EMPID as {coll.id}</literal></entry>
</row>
<row>
<entry>L'<27>l<EFBFBD>ment d'une collection</entry>
<entry><literal>{[aliasname].element}</literal></entry>
<entry><literal>XID as {coll.element}</literal></entry>
</row>
<row>
<entry>Propri<EFBFBD>t<EFBFBD> d'un <20>l<EFBFBD>ment de collection</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
<entry><literal>NAME as {coll.element.name}</literal></entry>
</row>
<row>
<entry>Toutes les propri<72>t<EFBFBD>s d'un <20>l<EFBFBD>ment de collection</entry>
<entry><literal>{[aliasname].element.*}</literal></entry>
<entry><literal>{coll.element.*}</literal></entry>
</row>
<row>
<entry>Toutes les propri<72>t<EFBFBD>s d'une collection</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{coll.*}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
</sect2>
<sect2>
<title>Retour d'objet n'<27>tant pas des entit<69>s</title>
<para>Il est possible d'appliquer un ResultTransformer <20> une requ<71>te native SQL. Ce qui permet, par exemple, de
retourner des entit<69>s non g<>r<EFBFBD>es.</para>
<programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
<para>Cette requ<71>te sp<73>cifie:</para>
<itemizedlist>
<listitem>
<para>une requ<71>te SQL</para>
</listitem>
<listitem>
<para>un transformateur de r<>sultat</para>
</listitem>
</itemizedlist>
<para>
La requ<71>te pr<70>c<EFBFBD>dente retournera une liste de <literal>CatDTO</literal> qui auront <20>t<EFBFBD> instanci<63>s
et dans lesquelles les valeurs de NAME et BIRTHNAME auront <20>t<EFBFBD> inject<63>es dans les propri<72>t<EFBFBD>s ou champs
correspondants.
</para>
</sect2>
<sect2>
<title>G<EFBFBD>rer l'h<>ritage</title>
<para>Les requ<71>tes natives SQL pour les entit<69>s prenant part <20> un h<>ritage
doivent inclure toutes les propri<72>t<EFBFBD>s de la classe de base et de toutes
ses sous classes.</para>
</sect2>
<sect2>
<title>Param<EFBFBD>tres</title>
<para>Les requ<71>tes natives SQL supportent aussi les param<61>tres nomm<6D>s:</para>
<programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
</sect2>
</sect1>
<sect1 id="querysql-namedqueries" revision="3">
<title>Requ<EFBFBD>tes SQL nomm<6D>es</title>
<para>
Les requ<71>tes SQL nomm<6D>es peuvent <20>tre d<>finies dans le document de mapping
et appel<65>es exactement de la m<>me mani<6E>re qu'un requ<71>te HQL nomm<6D>e. Dans ce
cas, nous <emphasis>n'avons pas besoin</emphasis> d'appeler <literal>addEntity()</literal>.
</para>
<programlisting>&lt;sql-query name="persons"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
<programlisting>List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();</programlisting>
<para>
Les <20>l<EFBFBD>ments <literal>&lt;return-join&gt;</literal> et
<literal>&lt;load-collection&gt;</literal> sont respectivement utilis<69>s pour lier
des associations et d<>finir des requ<71>tes qui initialisent des collections.
</para>
<programlisting>&lt;sql-query name="personsWith"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
&lt;return-join alias="address" property="person.mailingAddress"/&gt;
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
<para>
Une requ<71>te SQL nomm<6D>e peut retourner une valeur scalaire. Vous devez
sp<73>cifier l'alias de colonne et le type Hibernate utilisant l'<27>l<EFBFBD>ment
<literal>&lt;return-scalar&gt;</literal> :</para>
<programlisting>&lt;sql-query name="mySqlQuery"&gt;
&lt;return-scalar column="name" type="string"/&gt;
&lt;return-scalar column="age" type="long"/&gt;
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
&lt;/sql-query&gt;</programlisting>
<para>
Vous pouvez externaliser les informations de mapping des r<>sultats dans un
<20>l<EFBFBD>ment <literal>&lt;resultset&gt;</literal> pour soit les r<>utiliser
dans diff<66>rentes requ<71>tes nomm<6D>es, soit <20> travers l'API
<literal>setResultSetMapping()</literal>.
</para>
<programlisting>&lt;resultset name="personAddress"&gt;
&lt;return alias="person" class="eg.Person"/&gt;
&lt;return-join alias="address" property="person.mailingAddress"/&gt;
&lt;/resultset&gt;
&lt;sql-query name="personsWith" resultset-ref="personAddress"&gt;
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
&lt;/sql-query&gt;</programlisting>
<sect2 id="propertyresults">
<title>Utilisation de return-property pour sp<73>cifier explicitement les noms des colonnes/alias</title>
<para>
Avec <literal>&lt;return-property&gt;</literal> vous pouvez explicitement dire
<20> Hibernate quels alias de colonne utiliser, plutot que d'employer la syntaxe
<literal>{}</literal> pour laisser Hibernate injecter ses propres alias.
</para>
<programlisting>&lt;sql-query name="mySqlQuery"&gt;
&lt;return alias="person" class="eg.Person"&gt;
&lt;return-property name="name" column="myName"/&gt;
&lt;return-property name="age" column="myAge"/&gt;
&lt;return-property name="sex" column="mySex"/&gt;
&lt;/return&gt;
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
&lt;/sql-query&gt;
</programlisting>
<para>
<literal>&lt;return-property&gt;</literal> fonctionne aussi avec de
multiple colonnes. Cela r<>sout une limitation de la syntaxe <literal>{}</literal>
qui ne peut pas permettre une bonne granularit<69> des propri<72>t<EFBFBD>s multi-colonnes.
</para>
<programlisting>&lt;sql-query name="organizationCurrentEmployments"&gt;
&lt;return alias="emp" class="Employment"&gt;
&lt;return-property name="salary"&gt;
&lt;return-column name="VALUE"/&gt;
&lt;return-column name="CURRENCY"/&gt;
&lt;/return-property&gt;
&lt;return-property name="endDate" column="myEndDate"/&gt;
&lt;/return&gt;
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
&lt;/sql-query&gt;</programlisting>
<para>
Notez que dans cet exemple nous avons utilis<69> <literal>&lt;return-property&gt;</literal>
en combinaison avec la syntaxe <literal>{}</literal> pour l'injection. Cela autorise les
utilisateurs <20> choisir comment ils veulent r<>f<EFBFBD>rencer les colonnes et les propri<72>t<EFBFBD>s.
</para>
<para>
Si votre mapping a un discriminant vous devez utiliser
<literal>&lt;return-discriminator&gt;</literal> pour sp<73>cifier la colonne
discriminante.
</para>
</sect2>
<sect2 id="sp_query" revision="1">
<title>Utilisation de proc<6F>dures stock<63>es pour les requ<71>tes</title>
<para>
Hibernate 3 introduit le support des requ<71>tes via proc<6F>dures stock<63>es et les fonctions.
La documentation suivante est valable pour les deux.
Les proc<6F>dures stock<63>es/fonctions doivent retourner l'ensemble de r<>sultats en tant que
premier param<61>tre sortant (NdT: "out-parameter") pour <20>tre capable de fonctionner
avec Hibernate. Un exemple d'une telle proc<6F>dure stock<63>e en Oracle 9 et
version sup<75>rieure :
</para>
<programlisting>CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;</programlisting>
<para>Pour utiliser cette requ<71>te dans Hibernate vous avez besoin de la mapper via une requ<71>te nomm<6D>e.</para>
<programlisting>&lt;sql-query name="selectAllEmployees_SP" callable="true"&gt;
&lt;return alias="emp" class="Employment"&gt;
&lt;return-property name="employee" column="EMPLOYEE"/&gt;
&lt;return-property name="employer" column="EMPLOYER"/&gt;
&lt;return-property name="startDate" column="STARTDATE"/&gt;
&lt;return-property name="endDate" column="ENDDATE"/&gt;
&lt;return-property name="regionCode" column="REGIONCODE"/&gt;
&lt;return-property name="id" column="EID"/&gt;
&lt;return-property name="salary"&gt;
&lt;return-column name="VALUE"/&gt;
&lt;return-column name="CURRENCY"/&gt;
&lt;/return-property&gt;
&lt;/return&gt;
{ ? = call selectAllEmployments() }
&lt;/sql-query&gt;</programlisting>
<para>
Notez que les proc<6F>dures stock<63>es retournent, pour le moment, seulement des
scalaires et des entit<69>s. <literal>&lt;return-join&gt;</literal> et
<literal>&lt;load-collection&gt;</literal> ne sont pas support<72>s.
</para>
<sect3 id="querysql-limits-storedprocedures" revision="1">
<title>R<EFBFBD>gles/limitations lors de l'utilisation des proc<6F>dures stock<63>es</title>
<para>
Pur utiliser des proc<6F>dures stock<63>es avec Hibernate, les proc<6F>dures doivent
suivre certaines r<>gles. Si elles ne suivent pas ces r<>gles, elles ne sont pas
utilisables avec Hibernate. Si vous voulez encore utiliser ces proc<6F>dures vous
devez les ex<65>cuter via <literal>session.connection()</literal>. Les r<>gles
sont diff<66>rentes pour chaque base de donn<6E>es, puisque les vendeurs de base
de donn<6E>es ont des s<>mantiques/syntaxes diff<66>rentes pour les proc<6F>dures stock<63>es.
</para>
<para>Les requ<71>tes de proc<6F>dures stock<63>es ne peuvent pas <20>tre pagin<69>es avec
<literal>setFirstResult()/setMaxResults()</literal>.</para>
<para>Pour Oracle les r<>gles suivantes s'appliquent :</para>
<itemizedlist spacing="compact">
<listitem>
<para>
La proc<6F>dure doit retourner un ensemble de r<>sultats. Le
prmeier param<61>tre d'une proc<6F>dure doit <20>tre un <literal>OUT</literal>
qui retourne un ensemble de r<>sultats. Ceci est fait en
retournant un <literal>SYS_REFCURSOR</literal> dans Oracle 9 ou 10. Dans
Oracle vous avez besoin de d<>finir un type <literal>REF CURSOR</literal>.</para>
</listitem>
</itemizedlist>
<para>Pour Sybase ou MS SQL server les r<>gles suivantes s'appliquent :</para>
<itemizedlist spacing="compact">
<listitem>
<para>La proc<6F>dure doit retourner un ensemble de r<>sultats. Notez que comme
ces serveurs peuvent retourner de multiples ensembles de r<>sultats et mettre <20> jour
des compteurs, Hibernate it<69>rera les r<>sultats et prendra le premier r<>sultat qui est
un ensemble de r<>sultat comme valeur de retour. Tout le reste sera ignor<6F>.</para>
</listitem>
<listitem>
<para>Si vous pouvez activer <literal>SET NOCOUNT ON</literal> dans votre proc<6F>dure,
elle sera probablement plus efficace, mais ce n'est pas une obligation.</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</sect1>
<sect1 id="querysql-cud">
<title>SQL personnalis<69> pour cr<63>er, mettre <20> jour et effacer</title>
<para>
Hibernate3 peut utiliser des expression SQL personnalis<69>es pour des op<6F>rations de cr<63>ation,
de mise <20> jour, et de suppression. Les objets persistants les classes et les collections
dans Hibernate contiennent d<>j<EFBFBD> un ensemble de cha<68>nes de caract<63>res g<>n<EFBFBD>r<EFBFBD>es lors de la
configuration (insertsql, deletesql, updatesql, etc). Les tages de mapping
<literal>&lt;sql-insert&gt;</literal>,
<literal>&lt;sql-delete&gt;</literal>, et
<literal>&lt;sql-update&gt;</literal> surchargent ces cha<68>nes de caract<63>res :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
&lt;generator class="increment"/&gt;
&lt;/id&gt;
&lt;property name="name" not-null="true"/&gt;
&lt;sql-insert&gt;INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )&lt;/sql-insert&gt;
&lt;sql-update&gt;UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?&lt;/sql-update&gt;
&lt;sql-delete&gt;DELETE FROM PERSON WHERE ID=?&lt;/sql-delete&gt;
&lt;/class&gt;</programlisting>
<para>Le SQL est directement ex<65>cut<75> dans votre base de donn<6E>es, donc vous <20>tes libre d'utiliser
le dialecte que vous souhaitez. Cela r<>duira bien s<>r la portabilit<69> de votre mapping si vous
utilisez du SQL sp<73>cifique <20> votre base de donn<6E>es.</para>
<para>Les proc<6F>dures stock<63>es sont support<72>es si l'attribut <literal>callable</literal> est param<61>tr<74> :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
&lt;generator class="increment"/&gt;
&lt;/id&gt;
&lt;property name="name" not-null="true"/&gt;
&lt;sql-insert callable="true"&gt;{call createPerson (?, ?)}&lt;/sql-insert&gt;
&lt;sql-delete callable="true"&gt;{? = call deletePerson (?)}&lt;/sql-delete&gt;
&lt;sql-update callable="true"&gt;{? = call updatePerson (?, ?)}&lt;/sql-update&gt;
&lt;/class&gt;</programlisting>
<para>L'ordre des param<61>tres positionnels est actuellement vital, car ils doivent <20>tre dans la
m<>me s<>quence qu'Hibernate les attend.</para>
<para>
Vous pouvez voir l'ordre attendu en activant les journaux de debug pour le
niveau <literal>org.hibernate.persister.entity</literal> level. Avec ce niveau activ<69>,
Hibernate imprimera le SQL statique qui est utilis<69> pour cr<63>er, mettre <20> jour,
supprimer, etc. des entit<69>s. (Pour voir la s<>quence attendue, rappelez-vous de ne pas
inclure votre SQL personnalis<69> dans les fichiers de mapping de mani<6E>re <20> surcharger le
SQL statique g<>n<EFBFBD>r<EFBFBD> par Hibernate.)</para>
<para>Les proc<6F>dures stock<63>es sont dans la plupart des cas (lire : il vaut mieux le faire)
requises pour retourner le nombre de lignes ins<6E>r<EFBFBD>es/mises <20> jour/supprim<69>es, puisque
Hibernate fait quelques v<>rifications de succ<63>s lors de l'ex<65>cution de l'expression.
Hibernate inscrit toujours la premi<6D>re expression comme un param<61>tre de sortie num<75>rique pour les
op<6F>rations CUD :</para>
<programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;</programlisting>
</sect1>
<sect1 id="querysql-load">
<title>SQL personnalis<69> pour le chargement</title>
<para>Vous pouvez aussi d<>clarer vos propres requ<71>tes SQL (ou HQL) pour le chargement d'entit<69> :</para>
<programlisting>&lt;sql-query name="person"&gt;
&lt;return alias="pers" class="Person" lock-mode="upgrade"/&gt;
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
&lt;/sql-query&gt;</programlisting>
<para>Ceci est juste une d<>claration de requ<71>te nomm<6D>e, comme vu plus t<>t. Vous pouvez r<>f<EFBFBD>rencer
cette requ<71>te nomm<6D>e dans un mapping de classe :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
&lt;generator class="increment"/&gt;
&lt;/id&gt;
&lt;property name="name" not-null="true"/&gt;
&lt;loader query-ref="person"/&gt;
&lt;/class&gt;</programlisting>
<para>Ceci fonctionne m<>me avec des proc<6F>dures stock<63>es.</para>
<para>Vous pouvez m<>me d<>finir une requ<71>te pour le chargement d'une collection :</para>
<programlisting>&lt;set name="employments" inverse="true"&gt;
&lt;key/&gt;
&lt;one-to-many class="Employment"/&gt;
&lt;loader query-ref="employments"/&gt;
&lt;/set&gt;</programlisting>
<programlisting>&lt;sql-query name="employments"&gt;
&lt;load-collection alias="emp" role="Person.employments"/&gt;
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
&lt;/sql-query&gt;</programlisting>
<para>Vous pourriez m<>me d<>finir un chargeur d'entit<69> qui charge une collection par jointure :</para>
<programlisting>&lt;sql-query name="person"&gt;
&lt;return alias="pers" class="Person"/&gt;
&lt;return-join alias="emp" property="pers.employments"/&gt;
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
&lt;/sql-query&gt;</programlisting>
</sect1>
</chapter>