update for 3.2 doc compliance
git-svn-id: https://svn.jboss.org/repos/hibernate/branches/Branch_3_2/Hibernate3/doc@10541 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
76d5f33a31
commit
ea0327c63d
|
@ -33,7 +33,7 @@
|
|||
<bookinfo>
|
||||
<title>HIBERNATE - Persistance relationnelle en Java standard</title>
|
||||
<subtitle>Documentation de référence d'Hibernate</subtitle>
|
||||
<releaseinfo>3.1final</releaseinfo>
|
||||
<releaseinfo>3.2final</releaseinfo>
|
||||
</bookinfo>
|
||||
|
||||
<toc/>
|
||||
|
|
|
@ -270,7 +270,7 @@
|
|||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="architecture-current-session" revision="1">
|
||||
<sect1 id="architecture-current-session" revision="2">
|
||||
<title>Sessions Contextuelles</title>
|
||||
<para>
|
||||
Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où
|
||||
|
@ -320,6 +320,14 @@
|
|||
<literal>org.hibernate.context.ThreadLocalSessionContext</literal> - les sessions
|
||||
courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>org.hibernate.context.ManagedSessionContext</literal> - les sessions
|
||||
courantes sont traquées par l'exécution du thread. Toutefois, vous êtes responsable
|
||||
de lier et délier une instance de <literal>Session</literal> avec les méthodes
|
||||
statiques de cette classes, qui n'ouvre, ne flush ou ne ferme jamais de <literal>Session</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
|
|
@ -86,7 +86,8 @@
|
|||
qui agissent sur le schéma de base de données exporté par l'outil de
|
||||
génération de schéma. (Par exemple l'attribut <literal>not-null</literal>.)
|
||||
</para>
|
||||
<sect2 id="mapping-declaration-doctype" revision="2">
|
||||
|
||||
<sect2 id="mapping-declaration-doctype" revision="3">
|
||||
<title>Doctype</title>
|
||||
<para>
|
||||
Tous les mappings XML devraient utiliser le doctype indiqué.
|
||||
|
@ -96,6 +97,56 @@
|
|||
des recherches de la DTD sur Internet, vérifiez votre déclaration de DTD par rapport
|
||||
au contenu de votre classpath.
|
||||
</para>
|
||||
|
||||
<sect3 id="mapping-declaration-entity-resolution">
|
||||
<title>EntityResolver</title>
|
||||
<para>
|
||||
Comme cité précédemment, Hibernate tentera de trouver les DTDs d'abord dans son classpath. Il
|
||||
réussit à faire cela en utilisant une implémentation particulière de <literal>org.xml.sax.EntityResolver</literal>
|
||||
avec le SAXReader qu'il utilise pour lire les fichiers xml. Cet <literal>EntityResolver</literal> particulier
|
||||
reconnait deux espaces de nommage systemId différents.
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
un <literal>espace de nommage hibernate</literal> est reconnu dès qu'un systemId commence par
|
||||
<literal>http://hibernate.sourceforge.net/</literal>; alors ces entités sont résolues via le
|
||||
classloader qui a chargé les classes Hibernate.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
un <literal>espace de nommage utilisateur</literal> est reconnu dès qu'un systemId utilise
|
||||
un protocol URL <literal>classpath://</literal>. Le résolveur tentera de résoudre ces entités
|
||||
via (1) le classloader du contexte du thread courant et (2) le classloader qui a chargé
|
||||
les classes Hibernate.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Un exemple d'utilisation de l'espace de nommage utilisateur:
|
||||
</para>
|
||||
<programlisting><![CDATA[<?xml version="1.0"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC
|
||||
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
|
||||
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
|
||||
<!ENTITY types SYSTEM "classpath://your/domain/types.xml">
|
||||
]>
|
||||
|
||||
<hibernate-mapping package="your.domain">
|
||||
<class name="MyEntity">
|
||||
<id name="id" type="my-custom-id-type">
|
||||
...
|
||||
</id>
|
||||
<class>
|
||||
&types;
|
||||
</hibernate-mapping>]]></programlisting>
|
||||
<para>
|
||||
Où <literal>types.xml</literal> est une ressource dans le package <literal>your.domain</literal>
|
||||
et qui contient un <xref linkend="mapping-types-custom">typedef</xref> particulier.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
<sect2 id="mapping-declaration-mapping" revision="3">
|
||||
<title>hibernate-mapping</title>
|
||||
|
@ -1103,7 +1154,7 @@
|
|||
utilisent des identifiants assignés ou des clefs composées !</emphasis>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="mapping-declaration-timestamp" revision="3" >
|
||||
<sect2 id="mapping-declaration-timestamp" revision="4" >
|
||||
<title>timestamp (optionnel)</title>
|
||||
<para>
|
||||
L'élément optionnel <literal><timestamp></literal> indique que la table contient des données
|
||||
|
@ -1179,7 +1230,9 @@
|
|||
</programlistingco>
|
||||
<para>
|
||||
Notez que <literal><timestamp></literal> est équivalent à
|
||||
<literal><version type="timestamp"></literal>.
|
||||
<literal><version type="timestamp"></literal> et
|
||||
<literal><timestamp source="db"></literal> équivaut à
|
||||
<literal><version type="dbtimestamp"></literal>.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="mapping-declaration-property" revision="4">
|
||||
|
|
|
@ -33,6 +33,12 @@ session.close();]]></programlisting>
|
|||
|
||||
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
|
||||
|
||||
<para id="disablebatching" revision="1">
|
||||
Notez qu'Hibernate désactive, de manière transparente, l'insertion par paquet au
|
||||
niveau JDBC si vous utilisez un générateur d'identifiant de type
|
||||
<literal>identity</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Vous pourriez aussi vouloir faire cette sorte de travail dans un traitement où
|
||||
l'interaction avec le cache de second niveau est complètement désactivé :
|
||||
|
@ -150,7 +156,7 @@ session.close();]]></programlisting>
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="batch-direct" revision="2">
|
||||
<sect1 id="batch-direct" revision="3">
|
||||
<title>Opérations de style DML</title>
|
||||
|
||||
<para>
|
||||
|
@ -218,8 +224,32 @@ tx.commit();
|
|||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Pour exécuter un <literal>DELETE</literal> HQL, utilisez la même méthode
|
||||
<literal>Query.executeUpdate()</literal> :
|
||||
Par défaut, les statements HQL <literal>UPDATE</literal>, n'affectent pas la valeur des propriétés
|
||||
<xref linkend="mapping-declaration-version">version</xref> ou
|
||||
<xref linkend="mapping-declaration-timestamp">timestamp</xref>
|
||||
pour les entités affectées; ceci est compatible avec la spec EJB3. Toutefois,
|
||||
vous pouvez forcer Hibernate à mettre à jour les valeurs des propriétés
|
||||
<literal>version</literal> ou <literal>timestamp</literal> en utilisant le <literal>versioned update</literal>.
|
||||
Pour se faire, ajoutez le mot clé <literal>VERSIONED</literal> après le mot clé <literal>UPDATE</literal>.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
Transaction tx = session.beginTransaction();
|
||||
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
|
||||
int updatedEntities = s.createQuery( hqlUpdate )
|
||||
.setString( "newName", newName )
|
||||
.setString( "oldName", oldName )
|
||||
.executeUpdate();
|
||||
tx.commit();
|
||||
session.close();]]></programlisting>
|
||||
|
||||
<para>
|
||||
Notez que les types personnalisés (<literal>org.hibernate.usertype.UserVersionType</literal>)
|
||||
ne sont pas supportés en conjonction avec le statement <literal>update versioned</literal> statement.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Pour exécuter un HQL <literal>DELETE</literal>, utilisez la même méthode<literal>Query.executeUpdate()</literal>:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[Session session = sessionFactory.openSession();
|
||||
|
|
|
@ -938,7 +938,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
</tgroup>
|
||||
</table>
|
||||
|
||||
<table frame="topbot" id="configuration-misc-properties" revision="9">
|
||||
<table frame="topbot" id="configuration-misc-properties" revision="10">
|
||||
<title>Propriétés diverses</title>
|
||||
<tgroup cols="2">
|
||||
<colspec colname="c1" colwidth="1*"/>
|
||||
|
@ -962,7 +962,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
|
|||
<para>
|
||||
<emphasis role="strong">eg.</emphasis>
|
||||
<literal>jta</literal> | <literal>thread</literal> |
|
||||
<literal>custom.Class</literal>
|
||||
<literal>managed</literal> | <literal>custom.Class</literal>
|
||||
</para>
|
||||
</entry>
|
||||
</row>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
fonctionnalités génériques, et d'extensions de fonctionnalités d'Hibernate.
|
||||
</para>
|
||||
|
||||
<sect1 id="objectstate-interceptors" revision="2">
|
||||
<sect1 id="objectstate-interceptors" revision="3">
|
||||
<title>Intercepteurs</title>
|
||||
|
||||
<para>
|
||||
|
@ -115,21 +115,32 @@ public class AuditInterceptor extends EmptyInterceptor {
|
|||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
L'intercepteur doit être spécifié quand une session est créée.
|
||||
Il y a deux types d'intercepteurs: lié à la <literal>Session</literal> et
|
||||
lié à la <literal>SessionFactory</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Un intercepteur lié à la <literal>Session</literal> est défini
|
||||
lorsqu'une session est ouverte via l'invocation des méthodes surchargées SessionFactory.openSession()
|
||||
acceptant un <literal>Interceptor</literal> (comme argument).
|
||||
</para>
|
||||
<programlisting><![CDATA[Session session = sf.openSession( new AuditInterceptor() );]]></programlisting>
|
||||
|
||||
<para>
|
||||
Vous pouvez aussi mettre un intercepteur au niveau global, en utilisant l'objet <literal>Configuration</literal>.
|
||||
Dans ce cas, l'intercepteur doit être "threadsafe".
|
||||
Un intercepteur lié a <literal>SessionFactory</literal> est défini avec l'objet <literal>Configuration</literal>
|
||||
avant la construction de la <literal>SessionFactory</literal>. Dans ce cas, les intercepteurs fournis seront
|
||||
appliqués à toutes les sessions ouvertes pour cette <literal>SessionFactory</literal>; ceci est vrai
|
||||
à moins que la session ne soit ouverte en spécifiant l'intercepteur à utiliser.
|
||||
Les intercepteurs liés à la <literal>SessionFactory</literal> doivent être thread safe, faire attention
|
||||
à ne pas stocker des états spécifiques de la session puisque plusieurs sessions peuvent utiliser
|
||||
l'intercepteur de manière concurrente.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="objectstate-events" revision="3">
|
||||
<sect1 id="objectstate-events" revision="4">
|
||||
<title>Système d'événements</title>
|
||||
|
||||
<para>
|
||||
|
|
|
@ -461,7 +461,7 @@ dynamicSession.close()
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="persistent-classes-tuplizers" revision="0">
|
||||
<sect1 id="persistent-classes-tuplizers" revision="1">
|
||||
<title>Tuplizers</title>
|
||||
|
||||
<para>
|
||||
|
@ -473,7 +473,7 @@ dynamicSession.close()
|
|||
une telle structure de données. Par exemple, pour le mode d'entité POJO, le tuplizer correspondant
|
||||
sait comment créer le POJO à travers son constructeur et comment accéder aux propriétés du POJO
|
||||
utilisant les accesseurs de la propriété définie. Il y a deux types de Tuplizers haut niveau,
|
||||
représenté par les interfaces <literal>org.hibernate.tuple.EntityTuplizer</literal> et
|
||||
représentés par les interfaces <literal>org.hibernate.tuple.EntityTuplizer</literal> et
|
||||
<literal>org.hibernate.tuple.ComponentTuplizer</literal>. Les <literal>EntityTuplizer</literal>s
|
||||
sont responsables de la gestion des contrats mentionnés ci-dessus pour les entités, alors que
|
||||
les <literal>ComponentTuplizer</literal>s s'occupent des composants.
|
||||
|
@ -509,7 +509,7 @@ dynamicSession.close()
|
|||
|
||||
|
||||
public class CustomMapTuplizerImpl
|
||||
extends org.hibernate.tuple.DynamicMapEntityTuplizer {
|
||||
extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
|
||||
// override the buildInstantiator() method to plug in our custom map...
|
||||
protected final Instantiator buildInstantiator(
|
||||
org.hibernate.mapping.PersistentClass mappingInfo) {
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="queryhql-joins" revision="1">
|
||||
<sect1 id="queryhql-joins" revision="2">
|
||||
<title>Associations et jointures</title>
|
||||
|
||||
<para>
|
||||
|
@ -171,8 +171,9 @@
|
|||
Notez que la construction de <literal>fetch</literal> ne peut pas être utilisée dans les requêtes appelées par
|
||||
<literal>scroll()</literal> ou <literal>iterate()</literal>.
|
||||
<literal>fetch</literal> ne devrait pas non plus être utilisé avec <literal>setMaxResults()</literal> ou
|
||||
<literal>setFirstResult()</literal>. <literal>fetch</literal> ne peut pas non plus être utilisé avec une
|
||||
condition <literal>with</literal> ad hoc. Il est
|
||||
<literal>setFirstResult()</literal>, ces opérations étant basées sur le nombre de résultats qui contient
|
||||
généralement des doublons dès que des collections sont chargées.
|
||||
<literal>fetch</literal> ne peut pas non plus être utilisé avec une condition <literal>with</literal> ad hoc. Il est
|
||||
possible de créer un produit cartésien par jointure en récupérant plus d'une collection dans une requête,
|
||||
donc faites attention dans ce cas. Récupérer par jointure de multiples collections donne aussi parfois
|
||||
des résultats inattendus pour des mappings de bag, donc soyez prudent lorsque vous formulez vos requêtes dans de tels cas.
|
||||
|
@ -822,11 +823,11 @@ order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
|
|||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="queryhql-grouping">
|
||||
<sect1 id="queryhql-grouping" revision="1">
|
||||
<title>La clause group by</title>
|
||||
|
||||
<para>
|
||||
Si la requête retourne des valeurs aggrégées, celles ci peuvent être groupées par propriété ou composant :
|
||||
Si la requête retourne des valeurs aggrégées, celles-ci peuvent être groupées par propriété d'une classe retournée ou par composant :
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
|
||||
|
@ -847,20 +848,24 @@ group by cat.color
|
|||
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
|
||||
|
||||
<para>
|
||||
Les fonctions SQL et les fonctions d'aggrégations sont permises dans les clauses <literal>having</literal>
|
||||
et <literal>order by</literal>, si elles sont supportées par la base de données (ce que ne fait pas MySQL par exemple).
|
||||
Les fonctions SQL et les fonctions d'agrégat sont permises dans les clauses <literal>having</literal>
|
||||
et <literal>order by</literal>, si elles sont prises en charge par la base de données (ce que ne fait pas MySQL par exemple).
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[select cat
|
||||
from Cat cat
|
||||
join cat.kittens kitten
|
||||
group by cat
|
||||
group by cat.id, cat.name, cat.other, cat.properties
|
||||
having avg(kitten.weight) > 100
|
||||
order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
|
||||
|
||||
<para>
|
||||
Notez que ni la clause <literal>group by</literal> ni la clause
|
||||
<literal>order by</literal> ne peuvent contenir d'expressions arithmétiques.
|
||||
Notez aussi qu'Hibernate ne développe pas une entité faisant partie du regroupement,
|
||||
donc vous ne pouvez pas écrire <literal>group by cat</literal> si toutes
|
||||
les propriétés de <literal>cat</literal> sont non-agrégées. Vous devez
|
||||
lister toutes les propriétés non-agrégées explicitement.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<para>Hibernate3 vous permet de spécifier du SQL écrit à la main (incluant les procédures stockées)
|
||||
pour toutes les opérations de création, mise à jour, suppression et chargement.</para>
|
||||
|
||||
<sect1 id="querysql-creating" revision="3">
|
||||
<sect1 id="querysql-creating" revision="4">
|
||||
<title>Utiliser une <literal>SQLQuery</literal></title>
|
||||
|
||||
<para>L'exécution des requêtes en SQL natif est contrôlée par l'interface <literal>SQLQuery</literal>,
|
||||
|
@ -21,122 +21,218 @@
|
|||
Dans des cas extrêmement simples, nous pouvons utiliser la forme suivante :
|
||||
</para>
|
||||
|
||||
<programlisting>List cats = sess.createSQLQuery("select * from cats")
|
||||
.addEntity(Cat.class)
|
||||
.list();</programlisting>
|
||||
<sect2>
|
||||
<title>Requêtes scalaires</title>
|
||||
|
||||
<para>Cette requête a spécifié :</para>
|
||||
<para>La requête SQL la plus basique permet de récupérer une liste de (valeurs) scalaires.</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
|
||||
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
|
||||
]]></programlisting>
|
||||
|
||||
<para>Ces deux requêtes retourneront un tableau d'objets (Object[]) avec
|
||||
les valeurs scalaires de chacune des colonnes de la table CATS.
|
||||
Hibernate utilisera le ResultSetMetadata pour déduire l'ordre et le type
|
||||
des valeurs scalaires retournées.</para>
|
||||
|
||||
<para>Pour éviter l'overhead lié à <literal>ResultSetMetadata</literal> ou simplement pour
|
||||
être plus explicite dans ce qui est retourné, vous pouvez utiliser <literal>addScalar()</literal>.</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
|
||||
.addScalar("ID", Hibernate.LONG)
|
||||
.addScalar("NAME", Hibernate.STRING)
|
||||
.addScalar("BIRTHDATE", Hibernate.DATE)
|
||||
]]></programlisting>
|
||||
|
||||
<para>Cette requête spécifie:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>la requête SQL</para>
|
||||
<para>la chaîne de caractère SQL</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>l'entité retournée par la requête</para>
|
||||
<para>les colonnes et les types retournés</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Ici, les noms de colonne des résultats sont supposés être les mêmes que les noms de colonne spécifiés dans le
|
||||
document de mapping. Cela peut être problématique pour des requêtes SQL qui joignent de multiple tables, puisque
|
||||
les mêmes noms de colonne peuvent apparaître dans plus d'une table. La forme suivante n'est pas vulnérable à la
|
||||
duplication des noms de colonne :
|
||||
</para>
|
||||
<para>Cela retournera toujours un tableau d'objets, mais sans utiliser le
|
||||
<literal>ResultSetMetdata</literal>, mais récupèrera explicitement les colonnes
|
||||
ID, NAME and BIRTHDATE column étant de respectivement de type Long, String et Short,
|
||||
depuis le resultset sous jacent. Cela signifie aussi que seules ces colonnes seront
|
||||
retournées même si la requête utilise <literal>*</literal>
|
||||
et aurait pu retourner plus que les trois colonnes listées.</para>
|
||||
|
||||
<programlisting>List cats = sess.createSQLQuery("select {cat.*} from cats cat")
|
||||
.addEntity("cat", Cat.class)
|
||||
.list();</programlisting>
|
||||
<para>Il est possible de ne pas définir l'information sur le type pour toutes ou partie
|
||||
des calaires.</para>
|
||||
|
||||
<para>Cette requête a spécifié :</para>
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
|
||||
.addScalar("ID", Hibernate.LONG)
|
||||
.addScalar("NAME")
|
||||
.addScalar("BIRTHDATE")
|
||||
]]></programlisting>
|
||||
|
||||
<para>Il s'agit essentiellement de la même requête que précédemment, mais
|
||||
le <literal>ResultSetMetaData</literal> est utilisé pour décider des types de NAME
|
||||
et BIRTHDATE alors que le type de ID est explicitement spécifié.</para>
|
||||
|
||||
<para>Les java.sql.Types retournés par le ResultSetMetaData sont mappés aux type Hibernate
|
||||
via le Dialect. Si un type spécifique n'est pas mappé ou est mappé à un type non souhaité, il
|
||||
est possible de personnaliser en invoquant <literal>registerHibernateType</literal> dans
|
||||
le Dialect.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Requêtes d'entités</title>
|
||||
|
||||
<para>Les requêtes précédentes ne retournaient que des valeurs scalaires,
|
||||
retournant basiquement que les valeurs brutes du resultset. Ce qui suit montre
|
||||
comment récupérer des entités depuis une requête native SQL, grâce à
|
||||
<literal>addEntity()</literal>.</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
|
||||
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
|
||||
]]></programlisting>
|
||||
|
||||
<para>Cette requête spécifie:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>la requête SQL, avec un paramètre fictif pour Hibernate pour injecter les alias de colonne</para>
|
||||
<para>La chaîne de caractère de requête SQL</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>l'entité retournée par la requête, et son alias de table SQL</para>
|
||||
<para>L'entité retournée par la requête</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
La méthode <literal>addEntity()</literal> associe l'alias de la table SQL
|
||||
avec la classe de l'entité retournée, et détermine la forme de l'ensemble des résultats de la requête.
|
||||
</para>
|
||||
<para>Avec Cat mappé comme classe avec les colonnes ID, NAME
|
||||
et BIRTHDATE, les requêtes précédentes retournent toutes deux une liste
|
||||
où chaque élément est une entité Cat.</para>
|
||||
|
||||
<para>
|
||||
La méthode <literal>addJoin()</literal> peut être utilisée pour charger des associations vers d'autres
|
||||
entités et collections.
|
||||
</para>
|
||||
<para>Si l'entité est mappée avec un <literal>many-to-one</literal> vers
|
||||
une autre entité, il est requis de retourner aussi cette entité en exécutant
|
||||
la requête native, sinon une erreur "column not found" spécifique à la base de
|
||||
données sera soulevée. Les colonnes additionnelles seront automatiquement
|
||||
retournées en utilisant la notation *, mais nous préférons être explicites
|
||||
comme dans l'exemple suivant avec le <literal>many-to-one</literal> vers
|
||||
<literal>Dog</literal>:</para>
|
||||
|
||||
<programlisting>List cats = sess.createSQLQuery(
|
||||
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
|
||||
)
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
|
||||
]]></programlisting>
|
||||
|
||||
<para>Ceci permet à cat.getDog() de fonctionner normalement.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Gérer les associations et collections</title>
|
||||
|
||||
<para>Il est possible de charger agressivement <literal>Dog</literal> pour
|
||||
éviter le chargement de proxy qui signifie aller retour supplémentaire vers
|
||||
la base de données. Ceci est faisable via la méthode <literal>addJoin()</literal>,
|
||||
qui vous permet de joindre une association ou collection.</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
|
||||
.addEntity("cat", Cat.class)
|
||||
.addJoin("kitten", "cat.kittens")
|
||||
.list();</programlisting>
|
||||
.addJoin("cat.dog");
|
||||
]]></programlisting>
|
||||
|
||||
<para>
|
||||
Une requête SQL native pourrait retourner une simple valeur scalaire ou une combinaison de scalaires et d'entités.
|
||||
</para>
|
||||
<para>Dans cet exemple, les <literal>Cat</literal> retournés auront leur
|
||||
propriété <literal>dog</literal> entièrement initialisées sans aucun aller/retour
|
||||
supplémentaire vers la base de données. Notez que nous avons ajouté un alias
|
||||
("cat") pour être capable de spécifier la propriété cible de la jointure.
|
||||
Il est possible de faire la même jointure aggressive pour les collections, e.g. si le
|
||||
<literal>Cat</literal> a un one-to-many vers <literal>Dog</literal>.</para>
|
||||
|
||||
<programlisting>Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
|
||||
.addScalar("maxWeight", Hibernate.DOUBLE);
|
||||
.uniqueResult();</programlisting>
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
|
||||
.addEntity("cat", Cat.class)
|
||||
.addJoin("cat.dogs");
|
||||
]]></programlisting>
|
||||
|
||||
<para>Vous pouvez alternativement décrire les informations de mapping des résultats dans vos fichiers hbm
|
||||
et les utiliser pour vos requêtes.</para>
|
||||
<p>Nous arrivons aux limites de ce qui est possible avec les requêtes natives
|
||||
sans les modifier pour les rendre utilisables par Hibernate; les problèmes
|
||||
surviennent lorsque nous essayons de retourner des entités du même type ou
|
||||
lorsque les alias/colonnes par défaut ne sont plus suffisants..</p>
|
||||
</sect2>
|
||||
|
||||
<programlisting>List cats = sess.createSQLQuery(
|
||||
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
|
||||
)
|
||||
.setResultSetMapping("catAndKitten")
|
||||
.list();</programlisting>
|
||||
</sect1>
|
||||
<sect2>
|
||||
<title>Retour d'entités multiples</title>
|
||||
|
||||
<sect1 id="querysql-aliasreferences">
|
||||
<title>Alias et références de propriété</title>
|
||||
<para>Jusqu'à présent, les colonnes du resultset sont supposées être les mêmes
|
||||
que les colonnes spécifiées dans les fichiers de mapping. Ceci peut
|
||||
être problématique pour les requêtes SQL qui effectuent de multiples
|
||||
jointures vers différentes tables, puisque les mêmes colonnes peuvent
|
||||
apparaître dans plus d'une table.</para>
|
||||
|
||||
<para>
|
||||
La notation <literal>{cat.*}</literal> utilisée au-dessus est un raccourci pour "toutes les propriétés".
|
||||
Alternativement, vous pouvez lister explicitement les colonnes, mais même ce cas que nous laissons à Hibernate
|
||||
injecte des alias de colonne SQL pour chaque propriété. Le remplaçant pour un alias de colonne
|
||||
est juste le nom de la propriété qualifié par l'alias de la table.
|
||||
Dans l'exemple suivant, nous récupérons des <literal>Cat</literal>s à partir d'une table différente
|
||||
(<literal>cat_log</literal>) de celle déclarée dans les méta-données de mapping.
|
||||
Notez que nous pouvons même utiliser les alias de propriété dans la clause "where" si nous le souhaitons.
|
||||
</para>
|
||||
<para>L'injection d'alias de colonne est requis pour la requête suivante
|
||||
(qui risque de ne pas fonctionner):</para>
|
||||
|
||||
<para>
|
||||
La syntaxe <literal>{}</literal> <emphasis>n'est pas</emphasis> requise pour le requêtes nommées. Voir
|
||||
<xref linkend="querysql-namedqueries" />.
|
||||
</para>
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
|
||||
.addEntity("cat", Cat.class)
|
||||
.addEntity("mother", Cat.class)
|
||||
]]></programlisting>
|
||||
|
||||
<programlisting>String sql = "select cat.originalId as {cat.id}, " +
|
||||
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
|
||||
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
|
||||
"from cat_log cat where {cat.mate} = :catId"
|
||||
<para>Le but de cette requête est de retourner deux instances de Cat par ligne,
|
||||
un chat et sa mère. Cela échouera puisqu'il y a conflit de nom puisqu'ils sont
|
||||
mappés au même nom de colonne et que sur certaines base de données, les alias
|
||||
de colonnes retournés seront plutôt de la forme
|
||||
"c.ID", "c.NAME", etc. qui ne sont pas égaux aux colonnes spécifiées dans les
|
||||
mappings ("ID" and "NAME").</para>
|
||||
|
||||
<para>La forme suivante n'est pas vulnérable à la duplication des noms de colonnes:</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
|
||||
.addEntity("cat", Cat.class)
|
||||
.addEntity("mother", Cat.class)
|
||||
]]></programlisting>
|
||||
|
||||
<para>Cette requête spécifie:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>la requête SQL, avec des réceptacles pour qu'Hibernate injecte les alias de colonnes</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>les entités retournés par la requête</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Les notations {cat.*} et {mother.*} utilisées sont un équivalent à 'toutes les propriétés'.
|
||||
Alternativement, vous pouvez lister les colonnes explicitement, mais même pour ce cas, nous
|
||||
laissons Hibernate injecter les alias de colonne pour chaque propriété.
|
||||
Le réceptable pour un alias de colonne est simplement le nom de la propriété
|
||||
qualifié par l'alias de la table. Dans l'exemple suivant, nous récupérons
|
||||
les chats et leur mère depuis une table différentes (cat_log) de celle déclarée
|
||||
dans les mappings. Notez que nous pouvons aussi utiliser les alias de propriété
|
||||
dans la clause where si nous le voulons.</para>
|
||||
|
||||
<programlisting><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
|
||||
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
|
||||
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
|
||||
|
||||
List loggedCats = sess.createSQLQuery(sql)
|
||||
.addEntity("cat", Cat.class)
|
||||
.setLong("catId", catId)
|
||||
.list();</programlisting>
|
||||
.addEntity("mother", Cat.class).list()
|
||||
]]></programlisting>
|
||||
<sect3 id="querysql-aliasreferences" revision="2">
|
||||
<title>Références d'alias et de propriété</title>
|
||||
|
||||
<para>
|
||||
<emphasis>À noter :</emphasis> si vous listez chaque propriété explicitement, vous devez inclure
|
||||
toutes les propriétés de la classe <emphasis>et ses sous-classes</emphasis> !
|
||||
</para>
|
||||
<para>Pour la plupart des cas précédents, l'injection d'alias est requis,
|
||||
mais pour les requêtes relatives à des mappings plus complexes, comme
|
||||
les propriétés composite, les discriminants d'héritage, les collections etc., il
|
||||
y a des alias spécifiques à utiliser pour permettre à Hibernate l'injection
|
||||
des bons alias.</para>
|
||||
|
||||
<para>
|
||||
La table suivante montre les différentes possibilités d'utilisation de l'injection d'alias. À noter : les noms
|
||||
des alias dans le résultat sont des exemples, chaque alias aura un nom unique et probablement différent lors de l'utilisation.
|
||||
</para>
|
||||
<para>Le tableau suivant montre les diverses possiblités d'utilisation
|
||||
d'injection d'alias. Note: les noms d'alias dans le résultat sont des
|
||||
exemples, chaque alias aura un nom unique et probablement différent lorsqu'ils
|
||||
seront utilisés.</para>
|
||||
|
||||
<table frame="topbot" id="aliasinjection-summary">
|
||||
<title>Noms d'injection d'alias</title>
|
||||
<title>Nom d'injection d'alias</title>
|
||||
|
||||
<tgroup cols="4">
|
||||
<tgroup cols="3">
|
||||
<colspec colwidth="1*" />
|
||||
|
||||
<colspec colwidth="1*" />
|
||||
|
@ -155,15 +251,15 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>Une simple propriété</entry>
|
||||
<entry>Une propriété simple</entry>
|
||||
|
||||
<entry><literal>{[aliasname].[propertyname]}</literal></entry>
|
||||
<entry><literal>{[aliasname].[propertyname]</literal></entry>
|
||||
|
||||
<entry><literal>A_NAME as {item.name}</literal></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Une propriété composée</entry>
|
||||
<entry>Une propriété composite</entry>
|
||||
|
||||
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
|
||||
|
||||
|
@ -172,7 +268,7 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Discriminant d'une entité</entry>
|
||||
<entry>Discriminateur d'une entité</entry>
|
||||
|
||||
<entry><literal>{[aliasname].class}</literal></entry>
|
||||
|
||||
|
@ -188,7 +284,7 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Une clef de collection</entry>
|
||||
<entry>La clé d'une collection</entry>
|
||||
|
||||
<entry><literal>{[aliasname].key}</literal></entry>
|
||||
|
||||
|
@ -196,7 +292,7 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry>L'identifiant d'une collection</entry>
|
||||
<entry>L'id d'une collection</entry>
|
||||
|
||||
<entry><literal>{[aliasname].id}</literal></entry>
|
||||
|
||||
|
@ -209,12 +305,10 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
<entry><literal>{[aliasname].element}</literal></entry>
|
||||
|
||||
<entry><literal>XID as {coll.element}</literal></entry>
|
||||
|
||||
<entry></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Propriété de l'élément dans la collection</entry>
|
||||
<entry>Propriété d'un élément de collection</entry>
|
||||
|
||||
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
|
||||
|
||||
|
@ -222,7 +316,7 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Toutes les propriétés de l'élément dans la collection</entry>
|
||||
<entry>Toutes les propriétés d'un élément de collection</entry>
|
||||
|
||||
<entry><literal>{[aliasname].element.*}</literal></entry>
|
||||
|
||||
|
@ -230,7 +324,7 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Toutes les propriétés de la collection</entry>
|
||||
<entry>Toutes les propriétés d'une collection</entry>
|
||||
|
||||
<entry><literal>{[aliasname].*}</literal></entry>
|
||||
|
||||
|
@ -239,6 +333,57 @@ List loggedCats = sess.createSQLQuery(sql)
|
|||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Retour d'objet n'étant pas des entités</title>
|
||||
|
||||
<para>Il est possible d'appliquer un ResultTransformer à une requête native SQL. Ce qui permet, par exemple, de
|
||||
retourner des entités non gérées.</para>
|
||||
|
||||
<programlisting><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
|
||||
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
|
||||
|
||||
<para>Cette requête spécifie:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>une requête SQL</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>un transformateur de résultat</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
La requête précédente retournera une liste de <literal>CatDTO</literal> qui auront été instanciés
|
||||
et dans lesquelles les valeurs de NAME et BIRTHNAME auront été injectées dans les propriétés ou champs
|
||||
correspondants.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Gérer l'héritage</title>
|
||||
|
||||
<para>Les requêtes natives SQL pour les entités prenant part à un héritage
|
||||
doivent inclure toutes les propriétés de la classe de base et de toutes
|
||||
ses sous classes.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Paramètres</title>
|
||||
|
||||
<para>Les requêtes natives SQL supportent aussi les paramètres nommés:</para>
|
||||
|
||||
<programlisting><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
|
||||
List pusList = query.setString(0, "Pus%").list();
|
||||
|
||||
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
|
||||
List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="querysql-namedqueries" revision="3">
|
||||
|
|
|
@ -223,7 +223,7 @@ sess.refresh(cat); //re-read the state (after the trigger executes)]]></programl
|
|||
objets.
|
||||
</para>
|
||||
|
||||
<sect2 id="objectstate-querying-executing">
|
||||
<sect2 id="objectstate-querying-executing" revision="1">
|
||||
<title>Exécution de requêtes</title>
|
||||
|
||||
<para>
|
||||
|
@ -251,8 +251,11 @@ List kittens = session.createQuery(
|
|||
Cat mother = (Cat) session.createQuery(
|
||||
"select cat.mother from Cat as cat where cat = ?")
|
||||
.setEntity(0, izi)
|
||||
.uniqueResult();]]></programlisting>
|
||||
.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ête est généralement exécutée en invoquant <literal>list()</literal>,
|
||||
le résultat de la requête sera chargée complètement dans une collection en mémoire.
|
||||
|
@ -447,7 +450,7 @@ cats.close()]]></programlisting>
|
|||
|
||||
</sect3>
|
||||
|
||||
<sect3 id="objectstate-querying-executing-named">
|
||||
<sect3 id="objectstate-querying-executing-named" revision="1">
|
||||
<title>Externaliser des requêtes nommées</title>
|
||||
|
||||
<para>
|
||||
|
@ -477,6 +480,15 @@ List cats = q.list();]]></programlisting>
|
|||
migrer des requêtes existantes vers Hibernate en les plaçant dans les fichiers de mapping.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Notez aussi que la déclaration d'une requête dans un élément <literal><hibernate-mapping></literal>
|
||||
nécessite un nom globalement unique pour la requête, alors que la déclaration d'une requête
|
||||
dans une élément <literal><class></literal> est rendue unique de manière automatique par
|
||||
la mise en préfixe du nom entièrement de la classe, par exemple
|
||||
<literal>eg.Cat.ByNameAndMaximumWeight</literal>.
|
||||
</para>
|
||||
|
||||
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
|
|
@ -482,7 +482,7 @@ catch (RuntimeException e) {
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="transactions-demarcation-jta" revision="2">
|
||||
<sect2 id="transactions-demarcation-jta" revision="3">
|
||||
<title>Utilisation de JTA</title>
|
||||
|
||||
<para>Si votre couche de persistance s'exécute dans un serveur d'application (par exemple, derrière un EJB
|
||||
|
@ -514,19 +514,26 @@ finally {
|
|||
sess.close();
|
||||
}]]></programlisting>
|
||||
|
||||
<para>Ou encore, avec la gestion automatique de contexte:</para>
|
||||
|
||||
<para>
|
||||
Si vous souhaitez utiliser une <literal>Session</literal> couplée à la transaction, c'est à dire, utiliser
|
||||
la fonctionnalité <literal>getCurrentSession()</literal> pour la propagation facile du contexte,
|
||||
vous devrez utiliser l'API JTA <literal>UserTransaction</literal> directement:
|
||||
</para>
|
||||
<programlisting><![CDATA[// BMT idiom with getCurrentSession()
|
||||
try {
|
||||
factory.getCurrentSession().beginTransaction();
|
||||
UserTransaction tx = (UserTransaction)new InitialContext()
|
||||
.lookup("java:comp/UserTransaction");
|
||||
|
||||
// do some work
|
||||
...
|
||||
tx.begin();
|
||||
|
||||
factory.getCurrentSession().getTransaction().commit();
|
||||
// Do some work on Session bound to transaction
|
||||
factory.getCurrentSession().load(...);
|
||||
factory.getCurrentSession().persist(...);
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
factory.getCurrentSession().getTransaction().rollback();
|
||||
tx.rollback();
|
||||
throw e; // or display error message
|
||||
}]]></programlisting>
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="tutorial-firstapp" revision="1">
|
||||
<sect1 id="tutorial-firstapp" revision="2">
|
||||
<title>Partie 1 - Première application Hibernate</title>
|
||||
|
||||
<para>
|
||||
|
@ -339,7 +339,7 @@ public class Event {
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-firstapp-configuration" revision="1">
|
||||
<sect2 id="tutorial-firstapp-configuration" revision="2">
|
||||
<title>Configuration d'Hibernate</title>
|
||||
|
||||
<para>
|
||||
|
@ -353,7 +353,7 @@ public class Event {
|
|||
<para>
|
||||
Créez un répertoire appelé <literal>data</literal> à la racine du répertoire de développement -
|
||||
c'est là que HSQL DB stockera ses fichiers de données. Démarrez maintenant votre base de données
|
||||
en exécutant <literal>java -classpath lib/hsqldb.jar org.hsqldb.Server</literal> dans votre répertoire de travail.
|
||||
en exécutant <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal> dans votre répertoire de données.
|
||||
Vous observez qu'elle démarre et ouvre une socket TCP/IP, c'est là que notre application
|
||||
se connectera plus tard. Si vous souhaitez démarrez à partir d'une nouvelle base de données
|
||||
pour ce tutoriel (faites <literal>CTRL + C</literal> dans la fenêtre the window), effacez
|
||||
|
@ -614,7 +614,7 @@ build.xml]]></programlisting>
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-firstapp-workingpersistence" revision="4">
|
||||
<sect2 id="tutorial-firstapp-workingpersistence" revision="5">
|
||||
<title>Charger et stocker des objets</title>
|
||||
|
||||
<para>
|
||||
|
@ -655,6 +655,8 @@ public class EventManager {
|
|||
session.save(theEvent);
|
||||
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
|
@ -678,16 +680,33 @@ public class EventManager {
|
|||
<literal>SessionFactory</literal> (facile grâce à <literal>HibernateUtil</literal>).
|
||||
La méthode <literal>getCurrentSession()</literal> renvoie toujours l'unité de travail courante.
|
||||
Souvenez vous que nous avons basculé notre option de configuration au mécanisme basé sur le "thread"
|
||||
dans <literal>hibernate.cfg.xml</literal>. Par conséquent, le scope de l'unité de travail
|
||||
courante est le thread java courant d'exécution. Ceci n'est pas totalement vrai. Une
|
||||
<literal>Session</literal> commence lorsqu'elle est vraiment utilisée la première fois,
|
||||
Lorsque nous appelons pour la première fois <literal>getCurrentSession()</literal>.
|
||||
Ensuite, elle est liée, par Hibernate, au thread courant. Lorsque la transaction s'achève
|
||||
(commit ou rollback), Hibernate délie la <literal>Session</literal> du thread et la ferme
|
||||
pour vous. Si vous invoquez <literal>getCurrentSession()</literal> une autre fois, vous obtenez
|
||||
dans <literal>hibernate.cfg.xml</literal>. Par conséquent, l'unité de travail courante est liée
|
||||
au thread Java courant qui exécute notre application. Cependant, ce n'est pas tout, vous devez
|
||||
aussi considérer le scope, quand une unité de travail commence et quand elle finit.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Une <literal>Session</literal> commence lorsqu'elle est vraiment utilisée la première fois,
|
||||
lorsque nous appelons <literal>getCurrentSession()</literal> pour la première fois.
|
||||
Ensuite, elle est attachée par Hibernate au thread courant. Lorsque la transaction s'achève, par
|
||||
commit ou par rollback, Hibernate détache automatiquement la <literal>Session</literal> du thread et la ferme
|
||||
pour vous. Si vous invoquez <literal>getCurrentSession()</literal> une nouvelle fois, vous obtenez
|
||||
une nouvelle <literal>Session</literal> et pouvez entamer une nouvelle unité de travail.
|
||||
Ce modèle de programmation "<emphasis>thread-bound</emphasis>" est le moyen le plus
|
||||
populaire d'utiliser Hibernate.
|
||||
populaire d'utiliser Hibernate, puisqu'il permet un découpage flexible de votre code (le code délimitant
|
||||
les transactions peut être séparé du code accédant aux données, nous verrons cela plus loin dans ce tutorial).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A propos du scope de l'unité de travail, la <literal>Session</literal> Hibernate devrait-elle
|
||||
être utilisée pour exécuter une ou plusieurs opérations en base de données ? L'exemple ci-dessus
|
||||
utilise une <literal>Session</literal> pour une opération. C'est une pure coïncidence,
|
||||
l'exemple est n'est seulement pas assez complexe pour montrer d'autres approches. Le scope d'une
|
||||
<literal>Session</literal> Hibernate est flexible mais vous ne devriez jamais concevoir
|
||||
votre application de manière à utiliser une nouvelle <literal>Session</literal> Hibernate pour
|
||||
<emphasis>chaque</emphasis> opération en base de données. Donc même si vous le voyez quelques fois
|
||||
dans les exemples (très simplistes) suivants, considérez <emphasis>une session par operation</emphasis>
|
||||
comme un anti-pattern. Une véritable application (web) est montrée plus loin dans ce tutorial.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@ -868,7 +887,7 @@ public class Person {
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-associations-unidirset" revision="2">
|
||||
<sect2 id="tutorial-associations-unidirset" revision="3">
|
||||
<title>Une association unidirectionnelle basée sur Set</title>
|
||||
|
||||
<para>
|
||||
|
@ -920,7 +939,7 @@ public class Person {
|
|||
|
||||
<set name="events" table="PERSON_EVENT">
|
||||
<key column="PERSON_ID"/>
|
||||
<many-to-many column="EVENT_ID" class="Event"/>
|
||||
<many-to-many column="EVENT_ID" class="events.Event"/>
|
||||
</set>
|
||||
|
||||
</class>]]></programlisting>
|
||||
|
@ -957,7 +976,7 @@ public class Person {
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-associations-working" revision="1">
|
||||
<sect2 id="tutorial-associations-working" revision="2">
|
||||
<title>Travailler avec l'association</title>
|
||||
|
||||
<para>
|
||||
|
@ -1038,21 +1057,20 @@ public class Person {
|
|||
pour retourner cet identifiant).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Cela n'a pas grand intérêt dans notre situation, mais c'est un concept important qu'il vous faut concevoir
|
||||
dans votre application. Pour le moment, complétez cet excercice en ajoutant une nouvelle
|
||||
action à la méthode principale de l'<literal>EventManager</literal> et invoquez la depuis la ligne de commande.
|
||||
Si vous avez besoin des identifiants d'un client et d'un évènement - la méthode <literal>save()</literal>
|
||||
vous les retourne (vous devrez peut être modifier certaines méthodes précédentes pour retourner ces identifiants):
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
|
||||
Long eventId = mgr.createAndStoreEvent("My Event", new Date());
|
||||
Long personId = mgr.createAndStorePerson("Foo", "Bar");
|
||||
mgr.addPersonToEvent(personId, eventId);
|
||||
System.out.println("Added person " + personId + " to event " + eventId);]]></programlisting>
|
||||
|
||||
<para>
|
||||
Ce n'est pas très utile dans notre situation actuelle, mais c'est un concept important
|
||||
que vous pouvez mettre dans votre propre application.
|
||||
Pour le moment, complétez cet exercice en ajoutant une nouvelle action à la méthode
|
||||
principale des <literal>EventManager</literal>s et appelez la à partir de la ligne de
|
||||
commande. Si vous avez besoin des identifiants d'une personne et d'un événement - la
|
||||
méthode <literal>save()</literal> les retourne.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
C'était un exemple d'une association entre deux classes de même importance, deux entités.
|
||||
Comme mentionné plus tôt, il y a d'autres classes et d'autres types dans un modèle typique,
|
||||
|
@ -1296,7 +1314,7 @@ public void removeFromEvent(Event event) {
|
|||
la base de données, et fournir une formulaire HTML pour saisir d'autres évènements.
|
||||
</para>
|
||||
|
||||
<sect2 id="tutorial-webapp-servlet">
|
||||
<sect2 id="tutorial-webapp-servlet" revision="2">
|
||||
<title>Ecrire la servlet de base</title>
|
||||
|
||||
<para>
|
||||
|
@ -1309,18 +1327,9 @@ public void removeFromEvent(Event event) {
|
|||
|
||||
public class EventManagerServlet extends HttpServlet {
|
||||
|
||||
private final SimpleDateFormat dateFormatter =
|
||||
new SimpleDateFormat("dd.MM.yyyy");
|
||||
|
||||
// Servlet code
|
||||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
Le <literal>dateFormatter</literal> est un outil que nous utiliserons plus tard pour convertir les objets
|
||||
<literal>Date</literal> depuis et vers des chaines de caractères. Il est propice de n'avoir qu'un
|
||||
formatter comme membre de la servlet.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
La servlet n'accepte que les requêtes HTTP <literal>GET</literal>, la méthode à implémenter est donc
|
||||
<literal>doGet()</literal> :
|
||||
|
@ -1330,14 +1339,16 @@ public class EventManagerServlet extends HttpServlet {
|
|||
HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
|
||||
|
||||
try {
|
||||
// Begin unit of work
|
||||
// Début de l'unité de travail
|
||||
HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().beginTransaction();
|
||||
|
||||
// Process request and render page...
|
||||
// Traitement de la requête et rendu de la page...
|
||||
|
||||
// End unit of work
|
||||
// Fin de l'unité de travail
|
||||
HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().getTransaction().commit();
|
||||
|
||||
|
@ -1350,14 +1361,21 @@ public class EventManagerServlet extends HttpServlet {
|
|||
}]]></programlisting>
|
||||
|
||||
<para>
|
||||
La pattern que nous utilisons ici est appelé <emphasis>session-per-request</emphasis>.
|
||||
Lorsqu'une requête touche la servlet, une nouvelle <literal>Session</literal> hibernate est
|
||||
Le pattern que nous utilisons ici est appelé <emphasis>session-per-request</emphasis>.
|
||||
Lorsqu'une requête appelle la servlet, une nouvelle <literal>Session</literal> Hibernate est
|
||||
ouverte à l'invocation de <literal>getCurrentSession()</literal> sur la
|
||||
<literal>SessionFactory</literal>. Ensuite, une transaction avec la base de données est démarrée —
|
||||
tous les accès à la base de données interviennent au sein de la transactiton, peu importe que les données
|
||||
tous les accès à la base de données interviennent au sein de la transaction, peu importe que les données
|
||||
soient lues ou écrites (nous n'utilisons pas le mode auto-commit dans les applications).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>N'utilisez pas</emphasis> une nouvelle <literal>Session</literal> Hibernate pour
|
||||
chaque opération en base de données. Utilisez une <literal>Session</literal> Hibernate qui
|
||||
porte sur l'ensemble de la requête. Utlisez <literal>getCurrentSession()</literal>,
|
||||
ainsi elle est automatiquement attachée au thread Java courant.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Ensuite, les actions possibles de la requêtes sont exécutées et la réponse HTML
|
||||
est rendue. Nous en parlerons plus tard.
|
||||
|
@ -1367,7 +1385,7 @@ public class EventManagerServlet extends HttpServlet {
|
|||
Enfin, l'unité de travail s'achève lorsque l'exécution et le rendu sont achevés.
|
||||
Si un problème survient lors de ces deux phases, une exception est soulevée et la
|
||||
transaction avec la base de données subit un rollback. Voila pour le pattern
|
||||
<literal>session-per-request</literal>. Au lieu d'un code de démarcation de transaction
|
||||
<literal>session-per-request</literal>. Au lieu d'avoir un code de délimitant les transactions
|
||||
au sein de chaque servlet, vous pouvez écrire un filtre de servlet.
|
||||
Voir le site Hibernate et le Wiki pour plus d'information sur ce pattern, appelé
|
||||
<emphasis>Open Session in View</emphasis> — vous en aurez besoin dès que vous
|
||||
|
@ -1376,7 +1394,7 @@ public class EventManagerServlet extends HttpServlet {
|
|||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="tutorial-webapp-processing">
|
||||
<sect2 id="tutorial-webapp-processing" revision="1">
|
||||
<title>Procéder et rendre</title>
|
||||
|
||||
<para>
|
||||
|
@ -1434,7 +1452,8 @@ out.close();]]></programlisting>
|
|||
requête:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[private void listEvents(PrintWriter out) {
|
||||
<programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
|
||||
|
||||
List result = HibernateUtil.getSessionFactory()
|
||||
.getCurrentSession().createCriteria(Event.class).list();
|
||||
if (result.size() > 0) {
|
||||
|
|
Loading…
Reference in New Issue