hibernate-orm/doc/reference/fr/modules/session_api.xml

1242 lines
56 KiB
XML
Raw Normal View History

<?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>
<para>
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>
<sect2 id="objectstate-querying-executing" revision="1">
<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)
.uniqueResult();
Query mothersWithKittens = (Cat) session.createQuery(
"select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
<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>
<sect3 id="objectstate-querying-executing-named" revision="1">
<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>
<para>
Notez aussi que la d<>claration d'une requ<71>te dans un <20>l<EFBFBD>ment <literal>&lt;hibernate-mapping&gt;</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>&lt;class&gt;</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>
</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>&lt;version&gt;</literal> ou
<literal>&lt;timestamp&gt;</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>
<sect1 id="objectstate-transitive" revision="1">
<title>Persistance transitive</title>
<para>
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
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>&lt;many-to-one&gt;</literal> ou <literal>&lt;many-to-many&gt;</literal>. Les
cascades sont souvent utiles pour des associations
<literal>&lt;one-to-one&gt;</literal> et <literal>&lt;one-to-many&gt;</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>&lt;one-to-many&gt;</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>
<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>
</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>