2005-11-27 11:17:00 -05:00
|
|
|
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
|
|
|
<chapter id="objectstate">
|
|
|
|
|
<title>Travailler avec des objets</title>
|
|
|
|
|
<para>
|
|
|
|
|
Hibernate est une solution de mapping objet/relationnel compl<70>te qui ne masque pas
|
|
|
|
|
seulement au d<>velopppeur les d<>tails du syst<73>me de gestion de base de donn<6E>es sous-jacent,
|
|
|
|
|
mais offre aussi <emphasis>la gestion d'<27>tat</emphasis> des objets. C'est, contrairement
|
|
|
|
|
<20> la gestion de <literal>statements</literal> SQL dans les couches de persistance
|
|
|
|
|
habituelles JDBC/SQL, une vue orient<6E>e objet tr<74>s naturelle de la persistance dans les
|
|
|
|
|
applications Java.
|
|
|
|
|
</para>
|
|
|
|
|
|
2005-12-09 10:27:41 -05:00
|
|
|
|
<para>
|
2005-11-27 11:17:00 -05:00
|
|
|
|
En d'autres mots, les d<>veloppeurs d'applications Hibernate devrait toujours
|
|
|
|
|
r<>fl<66>chir <20> <emphasis>l'<27>tat</emphasis> de leurs objets, et pas n<>cessairement <20>
|
|
|
|
|
l'ex<65>cution des expressions SQL. Cette part est prise en charge pas Hibernate et
|
|
|
|
|
seulement importante pour les d<>veloppeurs d'applications lors du r<>glage de la
|
|
|
|
|
performance de leur syst<73>me.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-overview">
|
|
|
|
|
<title><EFBFBD>tats des objets Hibernate</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Hibernate d<>finit et comprend les <20>tats suivants :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<emphasis><EFBFBD>ph<EFBFBD>m<EFBFBD>re</emphasis> (NdT : transient) - un objet est <20>ph<70>m<EFBFBD>re s'il a juste
|
|
|
|
|
<20>t<EFBFBD> instanci<63> en utilisant l'op<6F>rateur <literal>new</literal>. Il n'a aucune
|
|
|
|
|
repr<70>sentation persistante dans la base de donn<6E>es et aucune valeur d'identifiant
|
|
|
|
|
n'a <20>t<EFBFBD> assign<67>e. Les instances <20>ph<70>m<EFBFBD>res seront d<>truites par le ramasse-miettes
|
|
|
|
|
si l'application n'en conserve aucune r<>f<EFBFBD>rence. Utilisez la <literal>Session</literal>
|
|
|
|
|
d'Hibernate pour rendre un objet persistant (et laisser Hibernate s'occuper des
|
|
|
|
|
expressions SQL qui ont besoin d'<27>tre ex<65>cut<75>es pour cette transistion).
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<emphasis>Persistant</emphasis> - une instance persistante a une repr<70>sentation dans la
|
|
|
|
|
base de donn<6E>es et une valeur d'identifiant. Elle pourrait avoir juste <20>t<EFBFBD> sauvegard<72>e
|
|
|
|
|
ou charg<72>e, pourtant, elle est par d<>finition dans la port<72>e d'une <literal>Session</literal>.
|
|
|
|
|
Hibernate d<>tectera n'importe quels changements effectu<74>s sur un objet dans l'<27>tat
|
|
|
|
|
persistant et synchronisera l'<27>tat avec la base de donn<6E>es lors de la fin l'unit<69> de travail.
|
|
|
|
|
Les d<>veloppeurs n'ex<65>cutent pas d'expressions <literal>UPDATE</literal> ou
|
|
|
|
|
<literal>DELETE</literal> manuelles lorsqu'un objet devrait <20>tre rendu <20>ph<70>m<EFBFBD>re.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<emphasis>D<EFBFBD>tach<EFBFBD></emphasis> - une instance d<>tach<63>e est un objet qui a <20>t<EFBFBD> persistant,
|
|
|
|
|
mais dont sa <literal>Session</literal> a <20>t<EFBFBD> ferm<72>e. La r<>f<EFBFBD>rence <20> l'objet est
|
|
|
|
|
encore valide, bien s<>r, et l'instance d<>tach<63>e pourrait m<>me <20>tre modifi<66>e dans cet
|
|
|
|
|
<20>tat. Une instance d<>tach<63>e peut <20>tre r<>attach<63>e <20> une nouvelle <literal>Session</literal>
|
|
|
|
|
plus tard dans le temps, la rendant (et toutes les modifications avec) de nouveau persistante.
|
|
|
|
|
Cette fonctionnalit<69> rend possible un mod<6F>le de programmation pour de longues unit<69>s de travail
|
|
|
|
|
qui requi<75>rent un temps de r<>flexion de l'utilisateur. Nous les appelons des <emphasis>conversations</emphasis>,
|
|
|
|
|
c'est-<2D>-dire une unit<69> de travail du point de vue de l'utilisateur.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Nous alons maintenant dicuster des <20>tats et des transitions d'<27>tat (et des m<>thodes
|
|
|
|
|
d'Hibernate qui d<>clenchent une transition) plus en d<>tails.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-makingpersistent" revision="1">
|
|
|
|
|
<title>Rendre des objets persistants</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les instances nouvellement instanci<63>es d'une classe persistante sont consid<69>r<EFBFBD>es
|
|
|
|
|
<emphasis><EFBFBD>ph<EFBFBD>m<EFBFBD>res</emphasis> par Hibernate. Nous pouvons rendre une instance
|
|
|
|
|
<20>ph<70>m<EFBFBD>re <emphasis>persistante</emphasis> en l'associant avec une session :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
|
|
|
|
|
fritz.setColor(Color.GINGER);
|
|
|
|
|
fritz.setSex('M');
|
|
|
|
|
fritz.setName("Fritz");
|
|
|
|
|
Long generatedId = (Long) sess.save(fritz);]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si <literal>Cat</literal> a un identifiant g<>n<EFBFBD>r<EFBFBD>, l'identifiant est g<>n<EFBFBD>r<EFBFBD> et assign<67>
|
|
|
|
|
au <literal>cat</literal> lorsque <literal>save()</literal> est appel<65>e. Si <literal>Cat</literal>
|
|
|
|
|
a un identifiant <literal>assigned</literal>, ou une clef compos<6F>e, l'identifiant
|
|
|
|
|
devrait <20>tre assign<67> <20> l'instance de <literal>cat</literal> avant d'appeler <literal>save()</literal>.
|
|
|
|
|
Vous pouvez aussi utiliser <literal>persist()</literal> <20> la place de<literal>save()</literal>,
|
|
|
|
|
avec la s<>mantique d<>finie plus t<>t dans le brouillon d'EJB3.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Alternativement, vous pouvez assigner l'identifiant en utilisant une version
|
|
|
|
|
surcharg<72>e de <literal>save()</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
|
|
|
|
|
pk.setColor(Color.TABBY);
|
|
|
|
|
pk.setSex('F');
|
|
|
|
|
pk.setName("PK");
|
|
|
|
|
pk.setKittens( new HashSet() );
|
|
|
|
|
pk.addKitten(fritz);
|
|
|
|
|
sess.save( pk, new Long(1234) );]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si l'objet que vous rendez persistant a des objets associ<63>s (par exemple,
|
|
|
|
|
la collection <literal>kittens</literal> dans l'exemple pr<70>c<EFBFBD>dent), ces objets
|
|
|
|
|
peuvent <20>tre rendus persistants dans n'importe quel ordre que vous souhaitez
|
|
|
|
|
<20> moins que vous ayez une contrainte <literal>NOT NULL</literal> sur la
|
|
|
|
|
colonne de la clef <20>trang<6E>re. Il n'y a jamais de risque de violer une
|
|
|
|
|
contrainte de clef <20>trang<6E>re. Cependant, vous pourriez violer une contrainte
|
|
|
|
|
<literal>NOT NULL</literal> si vous appeliez <literal>save()</literal> sur
|
|
|
|
|
les objets dans le mauvais ordre.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Habituellement, vous ne vous pr<70>occupez pas de ce d<>tail, puisque vous
|
|
|
|
|
utiliserez tr<74>s probablement la fonctionnalit<69> de <emphasis>persistance
|
|
|
|
|
transitive</emphasis> d'Hibernate pour sauvegarder les objets associ<63>s
|
|
|
|
|
automatiquement. Alors, m<>me les violations de contrainte <literal>NOT NULL</literal>
|
|
|
|
|
n'ont plus lieu - Hibernate prendra soin de tout. La persistance transitive est
|
|
|
|
|
trait<69>e plus loin dans ce chapitre.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-loading">
|
|
|
|
|
<title>Chargement d'un objet</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les m<>thodes <literal>load()</literal> de <literal>Session</literal> vous donnent
|
|
|
|
|
un moyen de r<>cup<75>rer une instance persistante si vous connaissez d<>j<EFBFBD> son identifiant.
|
|
|
|
|
<literal>load()</literal> prend un objet de classe et chargera l'<27>tat dans une instance
|
|
|
|
|
nouvellement instanci<63>e de cette classe, dans un <20>tat persistant.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// vous avez besoin d'envelopper les identiants primitifs
|
|
|
|
|
long pkId = 1234;
|
|
|
|
|
DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Alternativement, vous pouvez charger un <20>tat dans une instance donn<6E>e :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Cat cat = new DomesticCat();
|
|
|
|
|
// load pk's state into cat
|
|
|
|
|
sess.load( cat, new Long(pkId) );
|
|
|
|
|
Set kittens = cat.getKittens();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Notez que <literal>load()</literal> l<>vera une exception irr<72>cup<75>rable s'il
|
|
|
|
|
n'y a pas de ligne correspondante dans la base de donn<6E>es. Si la classe est mapp<70>e
|
|
|
|
|
avec un proxy, <literal>load()</literal> retourne juste un proxy non initialis<69> et
|
|
|
|
|
n'acc<63>de en fait pas <20> la base de donn<6E>es jusqu'<27> ce que vous invoquiez une
|
|
|
|
|
m<>thode du proxy. Ce comportement est tr<74>s utile si vous souhaitez cr<63>er
|
|
|
|
|
une association vers un objet sans r<>ellement le charger <20> partir de la base de
|
|
|
|
|
donn<6E>es. Cela permet aussi <20> de multiples instances d'<27>tre charg<72>es comme un lot
|
|
|
|
|
si <literal>batch-size</literal> est d<>fini pour le mapping de la classe.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si vous n'<27>tes pas certain qu'une ligne correspondante existe, vous devriez
|
|
|
|
|
utiliser la m<>thode <literal>get()</literal>, laquelle acc<63>de <20> la base de
|
|
|
|
|
donn<6E>es imm<6D>diatement et retourne null s'il n'y a pas de ligne correspondante.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
|
|
|
|
|
if (cat==null) {
|
|
|
|
|
cat = new Cat();
|
|
|
|
|
sess.save(cat, id);
|
|
|
|
|
}
|
|
|
|
|
return cat;]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Vous pouvez m<>me charger un objet en employant un <literal>SELECT ... FOR UPDATE</literal> SQL,
|
|
|
|
|
en utilisant un <literal>LockMode</literal>. Voir la documentation de l'API pour plus d'informations.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Notez que n'importe quelles instances associ<63>es ou collections contenues
|
|
|
|
|
<emphasis>ne sont pas</emphasis> s<>lectionn<6E>es par <literal>FOR UPDATE</literal>,
|
|
|
|
|
<20> moins que vous ne d<>cidiez de sp<73>cifier <literal>lock</literal> ou <literal>all</literal>
|
|
|
|
|
en tant que style de cascade pour l'association.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Il est possible de re-charger un objet et toutes ses collections <20> n'importe quel moment,
|
|
|
|
|
en utilisant la m<>thode <literal>refresh()</literal>. C'est utile lorsque des "triggers" de
|
|
|
|
|
base de donn<6E>es sont utilis<69>s pour initiliser certains propri<72>t<EFBFBD>s de l'objet.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[sess.save(cat);
|
|
|
|
|
sess.flush(); //force the SQL INSERT
|
|
|
|
|
sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Une question importante appara<72>t g<>n<EFBFBD>ralement <20> ce point : combien (NdT : de donn<6E>es) Hibernate
|
|
|
|
|
charge-t-il de la base de donn<6E>es et combient de <literal>SELECT</literal>s utilisera-t-il ?
|
|
|
|
|
Cela d<>pent de la <emphasis>strat<EFBFBD>gie de r<>cup<75>ration</emphasis> et cela est expliqu<71> dans
|
|
|
|
|
<xref linkend="performance-fetching"/>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-querying" revision="1">
|
|
|
|
|
<title>Requ<EFBFBD>tage</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si vous ne connaissez par les identifiants des objets que vous recherchez, vous
|
|
|
|
|
avez besoin d'une requ<71>te. Hibernate supporte un langage de requ<71>tes orient<6E>es objet
|
|
|
|
|
facile <20> utiliser mais puissant. Pour la cr<63>ation de requ<71>tes par programmation,
|
|
|
|
|
Hibernate supporte une fonction de requ<71>tage sophistiqu<71> Criteria et Example (QBC et QBE).
|
|
|
|
|
Vous pouvez aussi exprimez votre requ<71>te dans le SQL natif de votre base de donn<6E>es,
|
|
|
|
|
avec un support optionnel d'Hibernate pour la conversion des ensembles de r<>sultats en
|
|
|
|
|
objets.
|
|
|
|
|
</para>
|
|
|
|
|
|
2006-10-02 14:27:05 -04:00
|
|
|
|
<sect2 id="objectstate-querying-executing" revision="1">
|
2005-11-27 11:17:00 -05:00
|
|
|
|
<title>Ex<EFBFBD>cution de requ<71>tes</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les requ<71>tes HQL et SQL natives sont repr<70>sent<6E>es avec une instance de <literal>org.hibernate.Query</literal>.
|
|
|
|
|
L'interface offre des m<>thodes pour la liaison des param<61>tres, la gestion des ensembles de resultats, et pour
|
|
|
|
|
l'ex<65>cution de la requ<71>te r<>elle. Vous obtenez toujours une <literal>Query</literal> en utilisant la
|
|
|
|
|
<literal>Session</literal> courante :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[List cats = session.createQuery(
|
|
|
|
|
"from Cat as cat where cat.birthdate < ?")
|
|
|
|
|
.setDate(0, date)
|
|
|
|
|
.list();
|
|
|
|
|
|
|
|
|
|
List mothers = session.createQuery(
|
|
|
|
|
"select mother from Cat as cat join cat.mother as mother where cat.name = ?")
|
|
|
|
|
.setString(0, name)
|
|
|
|
|
.list();
|
|
|
|
|
|
|
|
|
|
List kittens = session.createQuery(
|
|
|
|
|
"from Cat as cat where cat.mother = ?")
|
|
|
|
|
.setEntity(0, pk)
|
|
|
|
|
.list();
|
|
|
|
|
|
|
|
|
|
Cat mother = (Cat) session.createQuery(
|
|
|
|
|
"select cat.mother from Cat as cat where cat = ?")
|
|
|
|
|
.setEntity(0, izi)
|
2006-10-02 14:27:05 -04:00
|
|
|
|
.uniqueResult();
|
2005-11-27 11:17:00 -05:00
|
|
|
|
|
2006-10-02 14:27:05 -04:00
|
|
|
|
Query mothersWithKittens = (Cat) session.createQuery(
|
|
|
|
|
"select mother from Cat as mother left join fetch mother.kittens");
|
|
|
|
|
Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
|
2005-11-27 11:17:00 -05:00
|
|
|
|
<para>
|
|
|
|
|
Une requ<71>te est g<>n<EFBFBD>ralement ex<65>cut<75>e en invoquant <literal>list()</literal>,
|
|
|
|
|
le r<>sultat de la requ<71>te sera charg<72>e compl<70>tement dans une collection en m<>moire.
|
|
|
|
|
Les intances d'entit<69>s recup<75>r<EFBFBD>es par une requ<71>te sont dans un <20>tat persistant.
|
|
|
|
|
La m<>thode <literal>uniqueResult()</literal> offre un raccourci si vous
|
|
|
|
|
savez que votre requ<71>te retournera seulement un seul objet.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<sect3 id="objectstate-querying-executing-iterate">
|
|
|
|
|
<title>It<EFBFBD>ration de r<>sultats</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Occasionnellement, vous pourriez <20>tre capable d'obtenir de meilleures
|
|
|
|
|
performances en ex<65>cutant la requ<71>te avec la m<>thode <literal>iterate()</literal>.
|
|
|
|
|
Ce sera g<>n<EFBFBD>ralement seulement le cas si vous esp<73>rez que les intances r<>elles
|
|
|
|
|
d'entit<69> retourn<72>es par la requ<71>te soient d<>j<EFBFBD> charg<72>es dans la session ou le
|
|
|
|
|
cache de second niveau. Si elles ne sont pas cach<63>es, <literal>iterate()</literal>
|
|
|
|
|
sera plus lent que <literal>list()</literal> et pourrait n<>cessiter plusieurs
|
|
|
|
|
acc<63>s <20> la base de donn<6E>es pour une simple requ<71>te, g<>n<EFBFBD>ralement <emphasis>1</emphasis>
|
|
|
|
|
pour le select initial qui retourne seulement les identifiants, et <emphasis>n</emphasis>
|
|
|
|
|
selects suppl<70>mentaires pour initialiser les instances r<>elles.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// fetch ids
|
|
|
|
|
Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
|
|
|
|
|
while ( iter.hasNext() ) {
|
|
|
|
|
Qux qux = (Qux) iter.next(); // fetch the object
|
|
|
|
|
// something we couldnt express in the query
|
|
|
|
|
if ( qux.calculateComplicatedAlgorithm() ) {
|
|
|
|
|
// delete the current instance
|
|
|
|
|
iter.remove();
|
|
|
|
|
// dont need to process the rest
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}]]></programlisting>
|
|
|
|
|
</sect3>
|
|
|
|
|
|
|
|
|
|
<sect3 id="objectstate-querying-executing-tuples">
|
|
|
|
|
<title>Requ<EFBFBD>tes qui retournent des tuples</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les requ<71>tes d'Hibernate retournent parfois des tuples d'objets, auquel cas chaque tuple
|
|
|
|
|
est retourn<72> comme un tableau :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
|
|
|
|
|
"select kitten, mother from Cat kitten join kitten.mother mother")
|
|
|
|
|
.list()
|
|
|
|
|
.iterator();
|
|
|
|
|
|
|
|
|
|
while ( kittensAndMothers.hasNext() ) {
|
|
|
|
|
Object[] tuple = (Object[]) kittensAndMothers.next();
|
|
|
|
|
Cat kitten = tuple[0];
|
|
|
|
|
Cat mother = tuple[1];
|
|
|
|
|
....
|
|
|
|
|
}]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect3>
|
|
|
|
|
|
|
|
|
|
<sect3 id="objectstate-querying-executing-scalar" revision="1">
|
|
|
|
|
<title>R<EFBFBD>sultats scalaires</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Des requ<71>tes peuvent sp<73>cifier une propri<72>t<EFBFBD> d'une classe dans la clause <literal>select</literal>.
|
|
|
|
|
Elles peuvent m<>me appeler des fonctions d'aggr<67>gat SQL. Les propri<72>t<EFBFBD>s ou les aggr<67>gats sont
|
|
|
|
|
consid<69>r<EFBFBD>s comme des r<>sultats "scalaires" (et pas des entit<69>s dans un <20>tat persistant).
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Iterator results = sess.createQuery(
|
|
|
|
|
"select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
|
|
|
|
|
"group by cat.color")
|
|
|
|
|
.list()
|
|
|
|
|
.iterator();
|
|
|
|
|
|
|
|
|
|
while ( results.hasNext() ) {
|
|
|
|
|
Object[] row = (Object[]) results.next();
|
|
|
|
|
Color type = (Color) row[0];
|
|
|
|
|
Date oldest = (Date) row[1];
|
|
|
|
|
Integer count = (Integer) row[2];
|
|
|
|
|
.....
|
|
|
|
|
}]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect3>
|
|
|
|
|
|
|
|
|
|
<sect3 id="objectstate-querying-executing-parameters">
|
|
|
|
|
<title>Lier des param<61>tres</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Des m<>thodes de <literal>Query</literal> sont fournies pour lier des
|
|
|
|
|
valeurs <20> des param<61>tres nomm<6D>s ou <20> des param<61>tres de style JDBC <literal>?</literal>.
|
|
|
|
|
<emphasis>Contrairement <20> JDBC, les num<75>ros des param<61>tres d'Hibernate commencent <20> z<>ro.</emphasis>
|
|
|
|
|
Les param<61>tres nomm<6D>s sont des identifiants de la forme <literal>:nom</literal> dans la cha<68>ne de
|
|
|
|
|
caract<63>res de la requ<71>te. Les avantages des param<61>tres nomm<6D>s sont :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
les param<61>tres nomm<6D>s sont insensibles <20> l'ordre de leur place dans la cha<68>ne
|
|
|
|
|
de la requ<71>te
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
ils peuvent appara<72>tre plusieurs fois dans la m<>me requ<71>te
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
ils sont auto-document<6E>s
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[//param<61>tre nomme (pr<70>f<EFBFBD>r<EFBFBD>)
|
|
|
|
|
Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
|
|
|
|
|
q.setString("name", "Fritz");
|
|
|
|
|
Iterator cats = q.iterate();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[//param<61>tre positionnel
|
|
|
|
|
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
|
|
|
|
|
q.setString(0, "Izi");
|
|
|
|
|
Iterator cats = q.iterate();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[//liste de param<61>tres nomm<6D>s
|
|
|
|
|
List names = new ArrayList();
|
|
|
|
|
names.add("Izi");
|
|
|
|
|
names.add("Fritz");
|
|
|
|
|
Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
|
|
|
|
|
q.setParameterList("namesList", names);
|
|
|
|
|
List cats = q.list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect3>
|
|
|
|
|
|
|
|
|
|
<sect3 id="objectstate-querying-executing-pagination">
|
|
|
|
|
<title>Pagination</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si vous avez besoin de sp<73>cifier des liens sur votre ensemble de r<>sultats (le nombre
|
|
|
|
|
maximum de lignes que vous voulez r<>cup<75>rez et/ou la premi<6D>re ligne que vous voulez r<>cup<75>rer)
|
|
|
|
|
vous devriez utiliser des m<>thodes de l'interface <literal>Query</literal> :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
|
|
|
|
|
q.setFirstResult(20);
|
|
|
|
|
q.setMaxResults(10);
|
|
|
|
|
List cats = q.list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Hibernate sait comment traduite cette requ<71>te de limite en SQL natif pour votre SGBD.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect3>
|
|
|
|
|
|
|
|
|
|
<sect3 id="objectstate-querying-executing-scrolling">
|
|
|
|
|
<title>It<EFBFBD>ration "scrollable"</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si votre connecteur JDBC supporte les <literal>ResultSet</literal>s "scrollables",
|
|
|
|
|
l'interface <literal>Query</literal> peut <20>tre utilis<69>e pour obtenir un objet
|
|
|
|
|
<literal>ScrollableResults</literal>, lequel permet une navigation flexible dans les
|
|
|
|
|
r<>sultats de la requ<71>te.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
|
|
|
|
|
"order by cat.name");
|
|
|
|
|
ScrollableResults cats = q.scroll();
|
|
|
|
|
if ( cats.first() ) {
|
|
|
|
|
|
|
|
|
|
// trouve le premier nom sur chaque page d'une liste alphab<61>tique de noms de chats
|
|
|
|
|
firstNamesOfPages = new ArrayList();
|
|
|
|
|
do {
|
|
|
|
|
String name = cats.getString(0);
|
|
|
|
|
firstNamesOfPages.add(name);
|
|
|
|
|
}
|
|
|
|
|
while ( cats.scroll(PAGE_SIZE) );
|
|
|
|
|
|
|
|
|
|
// Maintenant, obtiens la premi<6D>re page de chats
|
|
|
|
|
pageOfCats = new ArrayList();
|
|
|
|
|
cats.beforeFirst();
|
|
|
|
|
int i=0;
|
|
|
|
|
while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
cats.close()]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Notez qu'une connexion ouverte (et un curseur) est requise pour cette fonctionnalit<69>,
|
|
|
|
|
utilisez <literal>setMaxResult()</literal>/<literal>setFirstResult()</literal> si vous
|
|
|
|
|
avez besoin d'une fonctionnalit<69> de pagination hors ligne.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect3>
|
|
|
|
|
|
2006-10-02 14:27:05 -04:00
|
|
|
|
<sect3 id="objectstate-querying-executing-named" revision="1">
|
2005-11-27 11:17:00 -05:00
|
|
|
|
<title>Externaliser des requ<71>tes nomm<6D>es</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Vous pouvez aussi d<>finir des requ<71>tes nomm<6D>es dans le document de mapping.
|
|
|
|
|
(Souvenez-vous d'utiliser une section <literal>CDATA</literal> si votre requ<71>te
|
|
|
|
|
contient des caract<63>res qui pourraient <20>tre interpr<70>t<EFBFBD>s comme des <20>l<EFBFBD>ments XML.)
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[<query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[
|
|
|
|
|
from eg.DomesticCat as cat
|
|
|
|
|
where cat.name = ?
|
|
|
|
|
and cat.weight > ?
|
|
|
|
|
] ]></query>]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
La liaison de param<61>tres et l'ex<65>cution sont fait par programmation :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
|
|
|
|
|
q.setString(0, name);
|
|
|
|
|
q.setInt(1, minWeight);
|
|
|
|
|
List cats = q.list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Notez que le code r<>el du programme est ind<6E>pendant du langage de requ<71>te qui est
|
|
|
|
|
utilis<69>, vous pouvez aussi d<>finir des requ<71>tes SQL nativez dans les m<>ta-donn<6E>es, ou
|
|
|
|
|
migrer des requ<71>tes existantes vers Hibernate en les pla<6C>ant dans les fichiers de mapping.
|
|
|
|
|
</para>
|
2006-10-02 14:27:05 -04:00
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Notez aussi que la d<>claration d'une requ<71>te dans un <20>l<EFBFBD>ment <literal><hibernate-mapping></literal>
|
|
|
|
|
n<>cessite un nom globalement unique pour la requ<71>te, alors que la d<>claration d'une requ<71>te
|
|
|
|
|
dans une <20>l<EFBFBD>ment <literal><class></literal> est rendue unique de mani<6E>re automatique par
|
|
|
|
|
la mise en pr<70>fixe du nom enti<74>rement de la classe, par exemple
|
|
|
|
|
<literal>eg.Cat.ByNameAndMaximumWeight</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
|
2005-11-27 11:17:00 -05:00
|
|
|
|
|
|
|
|
|
</sect3>
|
|
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
|
|
<sect2 id="objectstate-filtering" revision="1">
|
|
|
|
|
<title>Filtrer des collections</title>
|
|
|
|
|
<para>
|
|
|
|
|
Un <emphasis>filtre</emphasis> de collection est un type sp<73>cial de requ<71>te qui peut <20>tre
|
|
|
|
|
appliqu<71> <20> une collection persistante ou <20> un tableau. La cha<68>ne de requ<71>te peut se r<>f<EFBFBD>rer <20>
|
|
|
|
|
<literal>this</literal>, correspondant <20> l'<27>l<EFBFBD>ment de la collection courant.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Collection blackKittens = session.createFilter(
|
|
|
|
|
pk.getKittens(),
|
|
|
|
|
"where this.color = ?")
|
|
|
|
|
.setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
|
|
|
|
|
.list()
|
|
|
|
|
);]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
La collection retourn<72>e est consid<69>r<EFBFBD>e comme un bag, et c'est une copie de la
|
|
|
|
|
collection donn<6E>e. La collection originale n'est pas modifi<66>e (c'est contraire
|
|
|
|
|
<20> l'implication du nom "filtre"; mais coh<6F>rent avec le comportement attendu).
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Observez que les filtres ne n<>cessitent pas une clause <literal>from</literal> (bien qu'ils
|
|
|
|
|
puissent en avoir une si besoin est). Les filtres ne sont pas limit<69>s <20> retourner des
|
|
|
|
|
<20>l<EFBFBD>ments de la collection eux-m<>mes.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
|
|
|
|
|
pk.getKittens(),
|
|
|
|
|
"select this.mate where this.color = eg.Color.BLACK.intValue")
|
|
|
|
|
.list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
M<>me une requ<71>te de filtre vide est utile, par exemple pour charger un sous-ensemble
|
|
|
|
|
d'<27>l<EFBFBD>ments dans une <20>norme collection :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Collection tenKittens = session.createFilter(
|
|
|
|
|
mother.getKittens(), "")
|
|
|
|
|
.setFirstResult(0).setMaxResults(10)
|
|
|
|
|
.list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
|
|
<sect2 id="objecstate-querying-criteria" revision="1">
|
|
|
|
|
<title>Requ<EFBFBD>tes Criteria</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
HQL est extr<74>mement puissant mais certains d<>veloppeurs pr<70>f<EFBFBD>rent construire des
|
|
|
|
|
requ<71>tes dynamiquement, en utilisant l'API orient<6E>e objet, plut<75>t que construire
|
|
|
|
|
des cha<68>nes de requ<71>tes. Hibernate fournit une API intuitive de requ<71>te <literal>Criteria</literal>
|
|
|
|
|
pour ces cas :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
|
|
|
|
|
crit.add( Expression.eq( "color", eg.Color.BLACK ) );
|
|
|
|
|
crit.setMaxResults(10);
|
|
|
|
|
List cats = crit.list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les APIs <literal>Criteria</literal> et <literal>Example</literal> associ<63> sont
|
|
|
|
|
trait<69>es plus en d<>tail dans <xref linkend="querycriteria"/>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
|
|
<sect2 id="objectstate-querying-nativesql" revision="2">
|
|
|
|
|
<title>Requ<EFBFBD>tes en SQL natif</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Vous pouvez exprimer une requ<71>te en SQL, en utilisant <literal>createSQLQuery()</literal>
|
|
|
|
|
et laisser Hibernate s'occuper du mapping des r<>sultats vers des objets. Notez que vous
|
|
|
|
|
pouvez n'importe quand appeler <literal>session.connection()</literal> et utiliser
|
|
|
|
|
directement la <literal>Connection</literal> JDBC. Si vous choisissez d'utiliser
|
|
|
|
|
l'API Hibernate, vous devez mettre les alias SQL entre accolades :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[List cats = session.createSQLQuery(
|
|
|
|
|
"SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
|
|
|
|
|
"cat",
|
|
|
|
|
Cat.class
|
|
|
|
|
).list();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[List cats = session.createSQLQuery(
|
|
|
|
|
"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
|
|
|
|
|
"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
|
|
|
|
|
"FROM CAT {cat} WHERE ROWNUM<10",
|
|
|
|
|
"cat",
|
|
|
|
|
Cat.class
|
|
|
|
|
).list()]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les requ<71>tes SQL peuvent contenir des param<61>tres nomm<6D>s et positionnels, comme des
|
|
|
|
|
requ<71>tes Hibernate. Plus d'informations <20> propos des requ<71>tes SQL natives dans Hibernate
|
|
|
|
|
peuvent <20>tre trouv<75>es dans <xref linkend="querysql"/>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-modifying" revision="1">
|
|
|
|
|
<title>Modifier des objets persistants</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les <emphasis>instances persistantes transactionnelles</emphasis> (c'est-<2D>-dire des objets
|
|
|
|
|
charg<72>s, sauvegard<72>s, cr<63><72>s ou requ<71>t<EFBFBD>s par la <literal>Session</literal>) peuvent <20>tre
|
|
|
|
|
manipul<75>es par l'application et n'importe quel changement vers l'<27>tat persistant sera
|
|
|
|
|
persist<73> lorsque la <literal>Session</literal> est <emphasis>"flush<73>e"</emphasis> (trait<69>
|
|
|
|
|
plus tard dans ce chapitre). Il n'y a pas besoin d'appeler une m<>thode particuli<6C>re
|
|
|
|
|
(comme <literal>update()</literal>, qui a un but diff<66>rent) pour rendre vos modifications
|
|
|
|
|
persistantes. Donc la mani<6E>re la plus directe de mettre <20> jour l'<27>tat d'un objet est de
|
|
|
|
|
le charger avec <literal>load()</literal>, et puis le manipuler directement, tant que la
|
|
|
|
|
<literal>Session</literal> est ouverte :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
|
|
|
|
|
cat.setName("PK");
|
|
|
|
|
sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Parfois ce mod<6F>le de programmation est inefficace puisqu'il n<>cessiterait un
|
|
|
|
|
<literal>SELECT</literal> SQL (pour charger l'objet) et un <literal>UPDATE</literal>
|
|
|
|
|
SQL (pour persister son <20>tat mis <20> jour) dans la m<>me session. Aussi Hibernate offre
|
|
|
|
|
une autre approche, en utilisant des instances d<>tach<63>es.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
<emphasis>Notez que Hibernate n'offre par sa propre API pour l'ex<65>cution directe
|
|
|
|
|
d'expressions <literal>UPDATE</literal> ou <literal>DELETE</literal>. Hibernate
|
|
|
|
|
est un service de <emphasis>gestion d'<27>tat</emphasis>, vous n'avez pas <20> penser
|
|
|
|
|
aux <emphasis>expressions</emphasis> pour l'utiliser. JDBC est une API parfaite
|
|
|
|
|
pour ex<65>cuter des expressions SQL, vous pouvez obtenir une <literal>Connection</literal>
|
|
|
|
|
JDBC n'importe quand en appelant <literal>session.connection()</literal>. En outre,
|
|
|
|
|
la notion d'op<6F>rations de masse entre en conflit avec le mapping objet/relationnel
|
|
|
|
|
pour les applications orient<6E>es processus de transactions en ligne. Les futures
|
|
|
|
|
versions d'Hibernate peuvent cependant fournir des fonctions d'op<6F>ration de masse.
|
|
|
|
|
Voir <xref linkend="batch"/> pour les astuces possibles d'op<6F>rations group<75>es.</emphasis>
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-detached" revision="2">
|
|
|
|
|
<title>Modifier des objets d<>tach<63>s</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Beaucoup d'applications ont besoin de r<>cup<75>rer un objet dans une transaction,
|
|
|
|
|
l'envoyer <20> la couche interfac<61>e avec l'utilisateur pour les manipulations, puis
|
|
|
|
|
sauvegarder les changements dans une nouvelle transaction. Les applications
|
|
|
|
|
qui utilisent cette approche dans un environnement <20> haute concurrence utilisent
|
|
|
|
|
g<>n<EFBFBD>ralement des donn<6E>es versionn<6E>es pour assurer l'isolation pour les "longues"
|
|
|
|
|
unit<69>s de travail.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Hibernate supporte ce mod<6F>le en permettant pour le r<>attachement d'instances d<>tach<63>es
|
|
|
|
|
l'utilisation des m<>thodes <literal>Session.update()</literal> ou <literal>Session.merge()</literal> :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// dans la premi<6D>re session
|
|
|
|
|
Cat cat = (Cat) firstSession.load(Cat.class, catId);
|
|
|
|
|
Cat potentialMate = new Cat();
|
|
|
|
|
firstSession.save(potentialMate);
|
|
|
|
|
|
|
|
|
|
// dans une couche plus haute de l'application
|
|
|
|
|
cat.setMate(potentialMate);
|
|
|
|
|
|
|
|
|
|
// plus tard, dans une nouvelle session
|
|
|
|
|
secondSession.update(cat); // update cat
|
|
|
|
|
secondSession.update(mate); // update mate]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si le <literal>Cat</literal> avec l'identifiant <literal>catId</literal> avait d<>j<EFBFBD>
|
|
|
|
|
<20>t<EFBFBD> charg<72> par <literal>secondSession</literal> lorsque l'application a essay<61> de le
|
|
|
|
|
r<>attacher, une exception aurait <20>t<EFBFBD> lev<65>e.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Utilisez <literal>update()</literal> si vous <20>tes sure que la session ne contient pas
|
|
|
|
|
d<>j<EFBFBD> une instance persistante avec le m<>me identifiant, et <literal>merge()</literal>
|
|
|
|
|
si vous voulez fusionner vos modifications n'importe quand sans consid<69>rer l'<27>tat de
|
|
|
|
|
la session. En d'autres mots, <literal>update()</literal> est g<>n<EFBFBD>ralement la premi<6D>re m<>thode
|
|
|
|
|
que vous devriez appeler dans une session fra<72>che, pour s'assurer que le r<>attachement
|
|
|
|
|
de vos instances d<>tach<63>es est la premi<6D>re op<6F>ration qui est ex<65>cut<75>e.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
L'application devrait individuellement <literal>update()</literal> (NdT : mettre <20> jour)
|
|
|
|
|
les instances d<>tach<63>es accessibles depuis l'instance d<>tach<63>e donn<6E>e si et
|
|
|
|
|
<emphasis>seulement</emphasis> si elle veut que leur <20>tat soit aussi mis <20> jour. Ceci
|
|
|
|
|
peut <20>tre automatis<69> bien s<>r, en utilisant la <emphasis>persistance transitive</emphasis>,
|
|
|
|
|
voir <xref linkend="objectstate-transitive"/>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
La m<>thode <literal>lock()</literal> permet aussi <20> une application de r<>associer un
|
|
|
|
|
objet avec une nouvelle session. Pourtant, l'instance d<>tach<63>e doit <20>tre non modifi<66>e !
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[//r<>associe :
|
|
|
|
|
sess.lock(fritz, LockMode.NONE);
|
|
|
|
|
//fait une v<>rification de version, puis r<>associe :
|
|
|
|
|
sess.lock(izi, LockMode.READ);
|
|
|
|
|
//fait une v<>rification de version, en utilisant SELECT ... FOR UPDATE, puis r<>associe :
|
|
|
|
|
sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Notez que <literal>lock()</literal> peut <20>tre utilis<69> avec diff<66>rents
|
|
|
|
|
<literal>LockMode</literal>s, voir la documentation de l'API documentation et le chapitre
|
|
|
|
|
sur la gestion des transactions pour plus d'informations. Le r<>attachement n'est pas le seul
|
|
|
|
|
cas d'utilisation pour <literal>lock()</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
D'autres mod<6F>les pour de longues unit<69>s de travail sont trait<69>s dans <xref linkend="transactions-optimistic"/>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-saveorupdate">
|
|
|
|
|
<title>D<EFBFBD>tection automatique d'un <20>tat</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les utilisateurs d'Hibernate ont demand<6E> une m<>thode dont l'intention g<>n<EFBFBD>rale
|
|
|
|
|
serait soit de sauvegarder une instance <20>ph<70>m<EFBFBD>re en g<>n<EFBFBD>rant un nouvel identifiant,
|
|
|
|
|
soit mettre <20> jour/r<>attacher les instances d<>tach<63>es associ<63>es <20> l'identifiant courant.
|
|
|
|
|
La m<>thode <literal>saveOrUpdate()</literal> impl<70>mente cette fonctionnalit<69>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// dans la premi<6D>re session
|
|
|
|
|
Cat cat = (Cat) firstSession.load(Cat.class, catID);
|
|
|
|
|
|
|
|
|
|
// dans une partie plus haute de l'application
|
|
|
|
|
Cat mate = new Cat();
|
|
|
|
|
cat.setMate(mate);
|
|
|
|
|
|
|
|
|
|
// plus tard, dans une nouvelle session
|
|
|
|
|
secondSession.saveOrUpdate(cat); // met <20> jour un <20>tat existant (cat a un identifiant non-null)
|
|
|
|
|
secondSession.saveOrUpdate(mate); // sauvegarde les nouvelles instances (mate a un identiant null)]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
L'usage et la s<>mantique de <literal>saveOrUpdate()</literal> semble <20>tre confuse pour les
|
|
|
|
|
nouveaux utilisateurs. Premi<6D>rement, aussi longtemps que vous n'essayez pas d'utiliser des
|
|
|
|
|
instances d'une session dans une autre, vous ne devriez pas avoir besoin d'utiliser <literal>update()</literal>,
|
|
|
|
|
<literal>saveOrUpdate()</literal>, ou <literal>merge()</literal>. Certaines applications
|
|
|
|
|
n'utiliseront jamais ces m<>thodes.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
G<>n<EFBFBD>ralement <literal>update()</literal> ou <literal>saveOrUpdate()</literal> sont utilis<69>es dans
|
|
|
|
|
le sc<73>nario suivant :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
l'application charge un objet dans la premi<6D>re session
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
l'objet est pass<73> <20> la couche utilisateur
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
certaines modifications sont effectu<74>es sur l'objet
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
l'objet est retourn<72> <20> la couche logique m<>tier
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
l'application persiste ces modifications en appelant
|
|
|
|
|
<literal>update()</literal> dans une seconde sessin
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
<literal>saveOrUpdate()</literal> s'utilise dans le cas suivant :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
si l'objet est d<>j<EFBFBD> persistant dans cette session, ne rien faire
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
si un autre objet associ<63> <20> la session a le m<>me identifiant, lever une exception
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
si l'objet n'a pas de propri<72>t<EFBFBD> d'identifiant, appeler <literal>save()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
si l'identifiant de l'objet a une valeur assign<67>e <20> un objet nouvellement instanci<63>,
|
|
|
|
|
appeler <literal>save()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
si l'objet est versionn<6E> (par <literal><version></literal> ou
|
|
|
|
|
<literal><timestamp></literal>), et la valeur de la propri<72>t<EFBFBD> de version
|
|
|
|
|
est la m<>me valeur que celle assign<67>e <20> un objet nouvellement instanci<63>, appeler
|
|
|
|
|
<literal>save()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
sinon mettre <20> jour l'objet avec <literal>update()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
et <literal>merge()</literal> est tr<74>s diff<66>rent :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
s'il y a une instance persistante avec le m<>me identifiant couramment
|
|
|
|
|
associ<63>e <20> la session, copier l'<27>tat de l'objet donn<6E> dans l'instance persistante
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
s'il n'y a pas d'instance persistante associ<63>e <20> cette session, essayer de le charger
|
|
|
|
|
<20> partir de la base de donn<6E>es, ou cr<63>er une nouvelle instance persistante
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
l'instance persistante est retourn<72>e
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
l'instance donn<6E>e ne devient pas associ<63>e <20> la session, elle reste d<>tach<63>e
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-deleting" revision="1">
|
|
|
|
|
<title>Suppression d'objets persistants</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
<literal>Session.delete()</literal> supprimera l'<27>tat d'un objet de la base de donn<6E>es.
|
|
|
|
|
Bien s<>r, votre application pourrait encore conserver une r<>f<EFBFBD>rence vers un objet effac<61>.
|
|
|
|
|
Il est mieux de penser <20> <literal>delete()</literal> comme rendant une instance persistante
|
|
|
|
|
<20>ph<70>m<EFBFBD>re.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[sess.delete(cat);]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Vous pouvez effacer des objets dans l'ordre que vous voulez, sans risque de violations
|
|
|
|
|
de contrainte de clef <20>trang<6E>re. Il est encore possible de violer une contrainte <literal>NOT
|
|
|
|
|
NULL</literal> sur une colonne de clef <20>trang<6E>re en effa<66>ant des objets dans le
|
|
|
|
|
mauvais ordre, par exemple si vous effacer le parent, mais oubliez d'effacer les enfants.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-replicating" revision="1">
|
|
|
|
|
<title>R<EFBFBD>plication d'objets entre deux entrep<65>ts de donn<6E>es</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Il est occasionnellement utile de pouvoir prendre un graphe d'instances persistantes
|
|
|
|
|
et de les rendre persistantes dans un entrep<65>t diff<66>rent, sans reg<65>n<EFBFBD>rer les valeurs
|
|
|
|
|
des identifiants.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[//r<>cup<75>re un cat de la base de donn<6E>es
|
|
|
|
|
Session session1 = factory1.openSession();
|
|
|
|
|
Transaction tx1 = session1.beginTransaction();
|
|
|
|
|
Cat cat = session1.get(Cat.class, catId);
|
|
|
|
|
tx1.commit();
|
|
|
|
|
session1.close();
|
|
|
|
|
|
|
|
|
|
// r<>concilie la seconde base de donn<6E>es
|
|
|
|
|
Session session2 = factory2.openSession();
|
|
|
|
|
Transaction tx2 = session2.beginTransaction();
|
|
|
|
|
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
|
|
|
|
|
tx2.commit();
|
|
|
|
|
session2.close();]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Le <literal>ReplicationMode</literal> d<>termine comment <literal>replicate()</literal>
|
|
|
|
|
traitera les conflits avec les lignes existantes dans la base de donn<6E>es.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<literal>ReplicationMode.IGNORE</literal> - ignore l'objet s'il y a une ligne
|
|
|
|
|
existante dans la base de donn<6E>es avec le m<>me identifiant
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<literal>ReplicationMode.OVERWRITE</literal> - <20>crase n'importe quelle ligne existante
|
|
|
|
|
dans la base de donn<6E>es avec le m<>me identifiant
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<literal>ReplicationMode.EXCEPTION</literal> - l<>ve une exception s'il y une ligne dans
|
|
|
|
|
la base de donn<6E>es avec le m<>me identifiant
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
<literal>ReplicationMode.LATEST_VERSION</literal> - <20>crase la ligne si son num<75>ro de version
|
|
|
|
|
est plus petit que le num<75>ro de version de l'objet, ou ignore l'objet sinon
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les cas d'utilisation de cette fonctionnalit<69> incluent la r<>conciliation de donn<6E>es
|
|
|
|
|
entr<74>es dans diff<66>rentes base de donn<6E>es, l'extension des informations de configuration
|
|
|
|
|
du syst<73>me durant une mise <20> jour du produit, retour en arri<72>re sur les changements effectu<74>s
|
|
|
|
|
durant des transactions non-ACID, et plus.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-flushing">
|
|
|
|
|
<title>Flush de la session</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
De temps en temps la <literal>Session</literal> ex<65>cutera les expressions SQL
|
|
|
|
|
requises pour syncrhoniser l'<27>tat de la connexion JDBC avec l'<27>tat des objets
|
|
|
|
|
retenus en m<>moire. Ce processus, <emphasis>flush</emphasis>, arrive par d<>faut aux
|
|
|
|
|
points suivants :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
lors de certaines ex<65>cutions de requ<71>te
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
lors d'un appel <20> <literal>org.hibernate.Transaction.commit()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
lors d'un appel <20> <literal>Session.flush()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les expressions SQL sont effectu<74>es dans l'ordre suivant :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<orderedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
insertion des entit<69>s, dans le m<>me ordre que celui des
|
|
|
|
|
objets correspondants sauvegard<72>s par l'appel <20> <literal>Session.save()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
mise <20> jours des entit<69>s
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
suppression des collections
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
suppression, mise <20> jour et insertion des <20>l<EFBFBD>ments des collections
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
insertion des collections
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
suppression des entit<69>s, dans le m<>me ordre que celui des objets
|
|
|
|
|
correspondants qui ont <20>t<EFBFBD> supprim<69>s par l'appel <20> <literal>Session.delete()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
(Une exception est que des objets utilisant la g<>n<EFBFBD>ration <literal>native</literal>
|
|
|
|
|
d'identifiants sont ins<6E>r<EFBFBD>s lorsqu'ils sont sauvegard<72>s.)
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Except<70> lorsque vous appelez <literal>flush()</literal> explicitement, il n'y
|
|
|
|
|
absolument aucune garantie <20> propos de <emphasis>quand</emphasis> la <literal>Session</literal>
|
|
|
|
|
ex<65>cute les appels JDBC, seulement sur l'<emphasis>ordre</emphasis> dans lequel ils sont
|
|
|
|
|
ex<65>cut<75>s. Cependant, Hibernate garantit que <literal>Query.list(..)</literal> ne
|
|
|
|
|
retournera jamais de donn<6E>es p<>rim<69>es, ni des donn<6E>es fausses.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Il est possible de changer le comportement par d<>faut, donc que le flush se produise
|
|
|
|
|
moins fr<66>quemment. La classe <literal>FlushMode</literal> d<>finit trois modes diff<66>rents :
|
|
|
|
|
flush seulement lors du commit (et seulement quand l'API <literal>Transaction</literal>
|
|
|
|
|
d'Hibernate est utilis<69>e), flush automatiquement en utilisant la proc<6F>dure expliqu<71>e, ou
|
|
|
|
|
jamais de flush <20> moins que <literal>flush()</literal> soit appel<65>e explicitement.
|
|
|
|
|
Le dernier mode est utile pour l'ex<65>cution de longues unit<69>s de travail, o<> une
|
|
|
|
|
<literal>Session</literal> est gard<72>e ouverte et d<>connect<63>e pour un long moment
|
|
|
|
|
(voir <xref linkend="transactions-optimistic-longsession"/>).
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[sess = sf.openSession();
|
|
|
|
|
Transaction tx = sess.beginTransaction();
|
|
|
|
|
sess.setFlushMode(FlushMode.COMMIT); // permet aux requ<71>tes de retourner un <20>tat p<>rim<69>
|
|
|
|
|
|
|
|
|
|
Cat izi = (Cat) sess.load(Cat.class, id);
|
|
|
|
|
izi.setName(iznizi);
|
|
|
|
|
|
|
|
|
|
// pourrait retourner des donn<6E>es p<>rim<69>es
|
|
|
|
|
sess.find("from Cat as cat left outer join cat.kittens kitten");
|
|
|
|
|
|
|
|
|
|
// le changement pour izi n'est pas flush<73> !
|
|
|
|
|
...
|
|
|
|
|
tx.commit(); // le flush se produit]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Durant le flush, une exception peut se produire (par exemple, si une op<6F>ration de la
|
|
|
|
|
DML viole une contrainte). Puisque les exceptions de gestion impliquent une certaine
|
|
|
|
|
compr<70>hension du comportement transactionnel d'Hibernate, nous le traitons dans
|
|
|
|
|
<xref linkend="transactions"/>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
2005-12-09 10:27:41 -05:00
|
|
|
|
<sect1 id="objectstate-transitive" revision="1">
|
2005-11-27 11:17:00 -05:00
|
|
|
|
<title>Persistance transitive</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
2005-12-09 10:27:41 -05:00
|
|
|
|
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<63>s. Un cas habituel
|
2005-11-27 11:17:00 -05:00
|
|
|
|
est une relation parent/enfant. Consid<69>rez l'exemple suivant :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Si les enfants de la relation parent/enfant <20>taient des types de valeur (par exemple,
|
|
|
|
|
une collection d'adresses ou de cha<68>nes de caract<63>res), leur cycle de vie d<>pendraient
|
|
|
|
|
du parent et aucune action ne serait requise pour "cascader" facilement les
|
|
|
|
|
changements d'<27>tat. Si le parent est sauvegard<72>, les objets enfants de type de valeur sont
|
|
|
|
|
sauvegard<72>s <20>galement, si le parent est supprim<69>, les enfants sont supprim<69>s, etc. Ceci
|
|
|
|
|
fonctionne m<>me pour des op<6F>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<EFBFBD>rences partag<61>es, supprimera l'enfant de la base de donn<6E>es.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Maintenant consid<69>rez le m<>me sc<73>nario avec un parent et dont les objets enfants
|
|
|
|
|
sont des entit<69>s, et non des types de valeur (par exemple, des cat<61>gories et des
|
|
|
|
|
objets, ou un parent et des chatons). Les entit<69>s ont leur propre cycle de vie,
|
|
|
|
|
supportent les r<>f<EFBFBD>rences partag<61>es (donc supprimer une entit<69> de la collection
|
|
|
|
|
ne signifie pas qu'elle peut <20>tre supprim<69>e), et il n'y a par d<>faut pas de
|
|
|
|
|
cascade d'<27>tat d'une entit<69> vers n'importe quelle entit<69> associ<63>e. Hibernate
|
|
|
|
|
n'impl<70>mente pas la <emphasis>persistance par accessibilit<69></emphasis> par d<>faut.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Pour chaque op<6F>ration basique de la session d'Hibernate - incluant <literal>persist(), merge(),
|
|
|
|
|
saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal> - il y a un
|
|
|
|
|
style de cascade correspondant. Respectivement, les styles de cascade s'appellent <literal>persist,
|
|
|
|
|
merge, save-update, delete, lock, refresh, evict, replicate</literal>. Si vous voulez qu'une
|
|
|
|
|
op<6F>ration soit cascad<61>e le long d'une association, vous devez l'indiquer dans le document de
|
|
|
|
|
mapping. Par exemple :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Les styles de cascade peuvent <20>tre combin<69>s :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Vous pouvez m<>me utiliser <literal>cascade="all"</literal> pour sp<73>cifier que <emphasis>toutes</emphasis>
|
|
|
|
|
les op<6F>rations devraient <20>tre cascad<61>es le long de l'association. La valeur par d<>faut
|
|
|
|
|
<literal>cascade="none"</literal> sp<73>cifie qu'aucune op<6F>ration ne sera cascad<61>e.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Une style de cascade sp<73>cial, <literal>delete-orphan</literal>, s'applique seulement
|
|
|
|
|
aux associations un-vers-plusieurs, et indique que l'op<6F>ration <literal>delete()</literal>
|
|
|
|
|
devrait <20>tre appliqu<71>e <20> n'importe quel enfant qui est supprim<69> de l'association.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Recommandations :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Cela n'a g<>n<EFBFBD>ralement aucun sens d'activer la cascade sur une association
|
|
|
|
|
<literal><many-to-one></literal> ou <literal><many-to-many></literal>. Les
|
|
|
|
|
cascades sont souvent utiles pour des associations
|
|
|
|
|
<literal><one-to-one></literal> et <literal><one-to-many></literal>.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si la dur<75>e de vie de l'objet enfant est li<6C>e <20> la dur<75>e de vie de l'objet parent,
|
|
|
|
|
faites en un <emphasis>objet du cycle de vie</emphasis> en sp<73>cifiant
|
|
|
|
|
<literal>cascade="all,delete-orphan"</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
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 <20>viter quelques frappes, consid<69>rez l'utilisation de
|
|
|
|
|
<literal>cascade="persist,merge,save-update"</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Mapper une association (soit une simple association valu<6C>e, soit une collection) avec
|
|
|
|
|
<literal>cascade="all"</literal> marque l'association comme une relation de style
|
|
|
|
|
<emphasis>parent/enfant</emphasis> o<> la sauvegarde/mise <20> jour/suppression du parent
|
|
|
|
|
entra<72>ne la sauvegarde/mise <20> jour/suppression de l'enfant ou des enfants.
|
|
|
|
|
</para>
|
|
|
|
|
<para>
|
|
|
|
|
En outre, une simple r<>f<EFBFBD>rence <20> un enfant d'un parent persistant aura pour cons<6E>quence
|
|
|
|
|
la sauvegarde/mise <20> jour de l'enfant. Cette m<>taphore est cependant incompl<70>te. Un enfant
|
|
|
|
|
qui devient non r<>f<EFBFBD>renc<6E> par son parent <emphasis>n'est pas</emphasis> automatiquement
|
|
|
|
|
supprim<69>e, except<70> dans le cas d'une association <literal><one-to-many></literal>
|
|
|
|
|
mapp<70>e avec <literal>cascade="delete-orphan"</literal>. La s<>mantique pr<70>cise des op<6F>rations
|
|
|
|
|
de cascade pour une relation parent/enfant est la suivante :
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si un parent est pass<73> <20> <literal>persist()</literal>, tous les enfant sont pass<73>s <20>
|
|
|
|
|
<literal>persist()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si un parent est pass<73> <20> <literal>merge()</literal>, tous les enfants sont pass<73>s <20>
|
|
|
|
|
<literal>merge()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si un parent est pass<73> <20> <literal>save()</literal>, <literal>update()</literal> ou
|
|
|
|
|
<literal>saveOrUpdate()</literal>, tous les enfants sont pass<73>s <20> <literal>saveOrUpdate()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si un enfant d<>tach<63> ou <20>ph<70>m<EFBFBD>re devient r<>f<EFBFBD>renc<6E> par un parent persistant,
|
|
|
|
|
il est pass<73> <20> <literal>saveOrUpdate()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si un parent est supprim<69>, tous les enfants sont pass<73>s <20> <literal>delete()</literal>
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para>
|
|
|
|
|
Si un enfant est d<>r<EFBFBD>f<EFBFBD>renc<6E> par un parent persistant, <emphasis>rien de sp<73>cial
|
|
|
|
|
n'arrive</emphasis> - l'application devrait explicitement supprimer l'enfant si n<>cessaire -
|
|
|
|
|
<20> moins que <literal>cascade="delete-orphan"</literal> soit param<61>tr<74>,
|
|
|
|
|
au quel cas l'enfant "orphelin" est supprim<69>.
|
|
|
|
|
</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
2005-12-09 10:27:41 -05:00
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Enfin, la cascade des op<6F>rations peut <20>tre effectu<74>e sur un graphe donn<6E> lors
|
|
|
|
|
de l'<emphasis>appel de l'op<6F>ration</emphasis> or lors du <emphasis>flush</emphasis>
|
|
|
|
|
suivant. Toutes les op<6F>rations, lorsque cascad<61>es, le sont sur toutes les entit<69>s
|
|
|
|
|
associ<63>es atteignables lorsque l'op<6F>tation est ex<65>cut<75>e. Cependant
|
|
|
|
|
<literal>save-upate</literal> et <literal>delete-orphan</literal> sont cascad<61>es
|
|
|
|
|
<20> toutes les entit<69>s associ<63>es atteignables lors du flush de la
|
|
|
|
|
<literal>Session</literal>.
|
|
|
|
|
</para>
|
2005-11-27 11:17:00 -05:00
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
<sect1 id="objectstate-metadata">
|
|
|
|
|
<title>Utilisation des m<>ta-donn<6E>es</title>
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
|
Hibernate requiert un mod<6F>le de m<>ta-niveau tr<74>s riche de toutes les entit<69>s et types valu<6C>s.
|
|
|
|
|
De temps en temps, ce mod<6F>le est tr<74>s utile <20> l'application elle m<>me. Par exemple,
|
|
|
|
|
l'application pourrait utiliser les m<>ta-donn<6E>es d'Hibernate pour impl<70>menter un algorithme
|
|
|
|
|
de copie en profondeur "intelligent" qui comprendrait quels objets devraient copi<70>s
|
|
|
|
|
(par exemple les types de valeur mutables) et lesquels ne devraient pas l'<27>tre (par exemple
|
|
|
|
|
les types de valeurs immutables et, possiblement, les entit<69>s associ<63>es).
|
|
|
|
|
</para>
|
|
|
|
|
<para>
|
|
|
|
|
Hibernate expose les m<>ta-donn<6E>es via les interfaces <literal>ClassMetadata</literal>
|
|
|
|
|
et <literal>CollectionMetadata</literal> et la hi<68>rarchie <literal>Type</literal>.
|
|
|
|
|
Les instances des interfaces de m<>ta-donn<6E>es peuvent <20>tre obtenues <20> partir de la
|
|
|
|
|
<literal>SessionFactory</literal>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Cat fritz = ......;
|
|
|
|
|
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
|
|
|
|
|
|
|
|
|
|
Object[] propertyValues = catMeta.getPropertyValues(fritz);
|
|
|
|
|
String[] propertyNames = catMeta.getPropertyNames();
|
|
|
|
|
Type[] propertyTypes = catMeta.getPropertyTypes();
|
|
|
|
|
|
|
|
|
|
// r<>cup<75>re une Map de toutes les propri<72>t<EFBFBD>s qui ne sont pas des collections ou des associations
|
|
|
|
|
Map namedValues = new HashMap();
|
|
|
|
|
for ( int i=0; i<propertyNames.length; i++ ) {
|
|
|
|
|
if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
|
|
|
|
|
namedValues.put( propertyNames[i], propertyValues[i] );
|
|
|
|
|
}
|
|
|
|
|
}]]></programlisting>
|
|
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
|
|
</chapter>
|