prep for gettext conversion

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14114 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2007-10-19 04:36:47 +00:00
parent 3fbbda75d1
commit 426dccd83a
28 changed files with 4391 additions and 4409 deletions

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY versionNumber "3.3.0.alpha1">
<!ENTITY copyrightYear "2004">
@ -9,7 +9,7 @@
<bookinfo>
<title>HIBERNATE - Persistance relationnelle en Java standard</title>
<subtitle>Documentation de référence d'Hibernate</subtitle>
<subtitle>Documentation de référence d'Hibernate</subtitle>
<releaseinfo>&versionNumber;</releaseinfo>
<productnumber>&versionNumber;</productnumber>
<issuenum>1</issuenum>
@ -25,7 +25,6 @@
<year>&copyrightYear;</year>
<holder>&copyrightHolder;</holder>
</copyright>
<xi:include href="translators.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="legal_notice.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</bookinfo>

View File

@ -1,28 +1,15 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
~ Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, v. 2.1. This program is distributed in the
~ hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
~ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details. You should have received a
~ copy of the GNU Lesser General Public License, v.2.1 along with this
~ distribution; if not, write to the Free Software Foundation, Inc.,
~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
~
~ Red Hat Author(s): Steve Ebersole
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="architecture">
<title>Architecture</title>
<sect1 id="architecture-overview" revision="1">
<title>Généralités</title>
<title>Généralités</title>
<para>
Voici une vue (très) haut niveau de l'architecture d'Hibernate :
Voici une vue (très) haut niveau de l'architecture d'Hibernate :
</para>
<mediaobject>
@ -35,16 +22,16 @@
</mediaobject>
<para>
Ce diagramme montre Hibernate utilisant une base de données et des données
Ce diagramme montre Hibernate utilisant une base de données et des données
de configuration pour fournir un service de persistance (et des objets
persistants) à l'application.
persistants) à l'application.
</para>
<para>
Nous aimerions décrire une vue plus détaillée de l'architecture. Malheureusement,
Hibernate est flexible et supporte différentes approches. Nous allons en
montrer les deux extrêmes. L'architecture légère laisse l'application fournir
ses propres connexions JDBC et gérer ses propres transactions. Cette approche
Nous aimerions décrire une vue plus détaillée de l'architecture. Malheureusement,
Hibernate est flexible et supporte différentes approches. Nous allons en
montrer les deux extrêmes. L'architecture légère laisse l'application fournir
ses propres connexions JDBC et gérer ses propres transactions. Cette approche
utilise le minimum des APIs Hibernate :
</para>
@ -58,8 +45,8 @@
</mediaobject>
<para>
L'architecture la plus complète abstrait l'application des APIs JDBC/JTA
sous-jacentes et laisse Hibernate s'occuper des détails.
L'architecture la plus complète abstrait l'application des APIs JDBC/JTA
sous-jacentes et laisse Hibernate s'occuper des détails.
</para>
<mediaobject>
@ -72,7 +59,7 @@
</mediaobject>
<para>
Voici quelques définitions des objets des diagrammes :
Voici quelques définitions des objets des diagrammes :
<variablelist spacing="compact">
<varlistentry>
@ -80,10 +67,10 @@
<listitem>
<para>
Un cache threadsafe (immuable) des mappings vers une (et une seule) base
de données. Une factory (fabrique) de <literal>Session</literal> et un client
de données. Une factory (fabrique) de <literal>Session</literal> et un client
de <literal>ConnectionProvider</literal>. Peut contenir un cache optionnel de
données (de second niveau) qui est réutilisable entre les différentes transactions
que cela soit au sein du même processus (JVLM) ou par plusieurs n½uds d'un cluster.
données (de second niveau) qui est réutilisable entre les différentes transactions
que cela soit au sein du même processus (JVLM) ou par plusieurs nœuds d'un cluster.
</para>
</listitem>
</varlistentry>
@ -91,11 +78,11 @@
<term>Session (<literal>org.hibernate.Session</literal>)</term>
<listitem>
<para>
Un objet mono-threadé, à durée de vie courte, qui représente une conversation
entre l'application et l'entrepôt de persistance. Encapsule une connexion JDBC.
Un objet mono-threadé, à durée de vie courte, qui représente une conversation
entre l'application et l'entrepôt de persistance. Encapsule une connexion JDBC.
Factory (fabrique) des objets <literal>Transaction</literal>. Contient un cache
(de premier niveau) des objets persistants, ce cache est obligatoire. Il est
utilisé lors de la navigation dans le graphe d'objets ou lors de la récupération
utilisé lors de la navigation dans le graphe d'objets ou lors de la récupération
d'objets par leur identifiant.
</para>
</listitem>
@ -104,13 +91,13 @@
<term>Objets et Collections persistants</term>
<listitem>
<para>
Objets mono-threadés à vie courte contenant l'état de persistance
et la fonction métier. Ceux-ci sont en général les objets de type JavaBean
(ou POJOs) ; la seule particularité est qu'ils sont associés avec une (et
une seule) <literal>Session</literal>. Dès que la <literal>Session</literal>
est fermée, ils seront détachés et libres d'être utilisés par n'importe laquelle
des couches de l'application (ie. de et vers la présentation en tant que Data
Transfer Objects - DTO : objet de transfert de données).
Objets mono-threadés à vie courte contenant l'état de persistance
et la fonction métier. Ceux-ci sont en général les objets de type JavaBean
(ou POJOs) ; la seule particularité est qu'ils sont associés avec une (et
une seule) <literal>Session</literal>. Dès que la <literal>Session</literal>
est fermée, ils seront détachés et libres d'être utilisés par n'importe laquelle
des couches de l'application (ie. de et vers la présentation en tant que Data
Transfer Objects - DTO : objet de transfert de données).
</para>
</listitem>
</varlistentry>
@ -118,10 +105,10 @@
<term>Objets et collections transients</term>
<listitem>
<para>
Instances de classes persistantes qui ne sont actuellement pas associées à
une <literal>Session</literal>. Elles ont pu être instanciées par l'application
et ne pas avoir (encore) été persistées ou elle ont pu être instanciées par
une <literal>Session</literal> fermée.
Instances de classes persistantes qui ne sont actuellement pas associées à
une <literal>Session</literal>. Elles ont pu être instanciées par l'application
et ne pas avoir (encore) été persistées ou elle ont pu être instanciées par
une <literal>Session</literal> fermée.
</para>
</listitem>
</varlistentry>
@ -129,11 +116,11 @@
<term>Transaction (<literal>org.hibernate.Transaction</literal>)</term>
<listitem>
<para>
(Optionnel) Un objet mono-threadé à vie courte utilisé par l'application
pour définir une unité de travail atomique. Abstrait l'application des
(Optionnel) Un objet mono-threadé à vie courte utilisé par l'application
pour définir une unité de travail atomique. Abstrait l'application des
transactions sous-jacentes qu'elles soient JDBC, JTA ou CORBA. Une
<literal>Session</literal> peut fournir plusieurs <literal>Transaction</literal>s
dans certains cas. Toutefois, la délimitation des transactions, via l'API d'Hibernate
dans certains cas. Toutefois, la délimitation des transactions, via l'API d'Hibernate
ou par la <literal>Transaction</literal> sous-jacente, n'est jamais optionnelle!
</para>
</listitem>
@ -144,7 +131,7 @@
<para>
(Optionnel) Une fabrique de (pool de) connexions JDBC. Abstrait l'application
de la <literal>Datasource</literal> ou du <literal>DriverManager</literal> sous-jacent.
Non exposé à l'application, mais peut être étendu/implémenté par le développeur.
Non exposé à l'application, mais peut être étendu/implémenté par le développeur.
</para>
</listitem>
</varlistentry>
@ -153,7 +140,7 @@
<listitem>
<para>
(Optionnel) Une fabrique d'instances de <literal>Transaction</literal>. Non
exposé à l'application, mais peut être étendu/implémenté par le développeur.
exposé à l'application, mais peut être étendu/implémenté par le développeur.
</para>
</listitem>
</varlistentry>
@ -162,8 +149,8 @@
<listitem>
<para>
Hibernate fournit de nombreuses interfaces d'extensions optionnelles que
vous pouvez implémenter pour personnaliser le comportement de votre couche de persistance.
Reportez vous à la documentation de l'API pour plus de détails.
vous pouvez implémenter pour personnaliser le comportement de votre couche de persistance.
Reportez vous à la documentation de l'API pour plus de détails.
</para>
</listitem>
</varlistentry>
@ -171,7 +158,7 @@
</para>
<para>
Dans une architecture légère, l'application n'aura pas à utiliser les APIs
Dans une architecture légère, l'application n'aura pas à utiliser les APIs
<literal>Transaction</literal>/<literal>TransactionFactory</literal>
et/ou n'utilisera pas les APIs <literal>ConnectionProvider</literal>
pour utiliser JTA ou JDBC.
@ -181,9 +168,9 @@
<sect1 id="architecture-states" revision="1">
<title>Etats des instances</title>
<para>
Une instance d'une classe persistante peut être dans l'un des trois états suivants,
définis par rapport à un <emphasis>contexte de persistance</emphasis>.
L'objet <literal>Session</literal> d'hibernate correspond à ce concept de
Une instance d'une classe persistante peut être dans l'un des trois états suivants,
définis par rapport à un <emphasis>contexte de persistance</emphasis>.
L'objet <literal>Session</literal> d'hibernate correspond à ce concept de
contexte de persistance :
</para>
@ -192,8 +179,8 @@
<term>passager (transient)</term>
<listitem>
<para>
L'instance n'est pas et n'a jamais été associée à un contexte
de persistance. Elle ne possède pas d'identité persistante (valeur de clé primaire)
L'instance n'est pas et n'a jamais été associée à un contexte
de persistance. Elle ne possède pas d'identité persistante (valeur de clé primaire)
</para>
</listitem>
</varlistentry>
@ -201,26 +188,26 @@
<term>persistant</term>
<listitem>
<para>
L'instance est associée au contexte de persistance.
Elle possède une identité persistante (valeur de clé primaire)
et, peut-être, un enregistrement correspondant dans la base.
L'instance est associée au contexte de persistance.
Elle possède une identité persistante (valeur de clé primaire)
et, peut-être, un enregistrement correspondant dans la base.
Pour un contexte de persistance particulier, Hibernate
<emphasis>garantit</emphasis> que l'identité persistante
est équivalente à l'identité Java (emplacement mémoire de l'objet)
<emphasis>garantit</emphasis> que l'identité persistante
est équivalente à l'identité Java (emplacement mémoire de l'objet)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>détaché</term>
<term>détaché</term>
<listitem>
<para>
L'instance a été associée au contexte de persistance mais ce
contexte a été fermé, ou l'instance a été sérialisée vers un
autre processus. Elle possède une identité persistante et
peut-être un enregistrement correspondant dans la base.
Pour des instances détachées, Hibernate ne donne aucune
garantie sur la relation entre l'identité persistante et
l'identité Java.
L'instance a été associée au contexte de persistance mais ce
contexte a été fermé, ou l'instance a été sérialisée vers un
autre processus. Elle possède une identité persistante et
peut-être un enregistrement correspondant dans la base.
Pour des instances détachées, Hibernate ne donne aucune
garantie sur la relation entre l'identité persistante et
l'identité Java.
</para>
</listitem>
</varlistentry>
@ -228,39 +215,39 @@
</sect1>
<sect1 id="architecture-jmx" revision="1">
<title>Intégration JMX</title>
<title>Intégration JMX</title>
<para>
JMX est le standard J2EE de gestion des composants Java.
Hibernate peut être géré via un service JMX standard. Nous fournissons une implémentation
Hibernate peut être géré via un service JMX standard. Nous fournissons une implémentation
d'un MBean dans la distribution : <literal>org.hibernate.jmx.HibernateService</literal>.
</para>
<para>
Pour avoir un exemple sur la manière de déployer Hibernate en tant que service JMX dans le
serveur d'application JBoss Application Server, référez vous au guide utilisateur JBoss (JBoss User Guide).
Si vous déployez Hibernate via JMX sur JBoss AS, vous aurez également les bénéfices suivants :
Pour avoir un exemple sur la manière de déployer Hibernate en tant que service JMX dans le
serveur d'application JBoss Application Server, référez vous au guide utilisateur JBoss (JBoss User Guide).
Si vous déployez Hibernate via JMX sur JBoss AS, vous aurez également les bénéfices suivants :
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Gestion de la session :</emphasis> Le cycle de vie de la <literal>Session</literal>
Hibernate peut être automatiquement limitée à la portée d'une transaction JTA.
Hibernate peut être automatiquement limitée à la portée d'une transaction JTA.
Cela signifie que vous n'avez plus besoin d'ouvrir et de fermer la <literal>Session</literal>
manuellement, cela devient le travail de l'intercepteur EJB de JBoss. Vous n'avez
pas non plus à vous occuper des démarcations des transactions dans votre code (sauf
si vous voulez écrire une couche de persistance qui soit portable, dans ce cas vous
pas non plus à vous occuper des démarcations des transactions dans votre code (sauf
si vous voulez écrire une couche de persistance qui soit portable, dans ce cas vous
pouvez utiliser l'API optionnelle <literal>Transaction</literal> d'Hibernate).
Vous appelez l'<literal>HibernateContext</literal> pour accéder à la <literal>Session</literal>.
Vous appelez l'<literal>HibernateContext</literal> pour accéder à la <literal>Session</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Déploiement HAR :</emphasis> Habituellement vous déployez le service JMX
Hibernate en utilisant le descripteur de déploiement de JBoss (dans un fichier EAR et/ou un SAR),
<emphasis>Déploiement HAR :</emphasis> Habituellement vous déployez le service JMX
Hibernate en utilisant le descripteur de déploiement de JBoss (dans un fichier EAR et/ou un SAR),
il supporte toutes les options de configuration usuelles d'une <literal>SessionFactory</literal>
Hibernate. Cependant, vous devez toujours nommer tous vos fichiers de mapping dans le
descripteur de déploiement. Si vous décidez d'utiliser le déploiement optionnel sous forme
de HAR, JBoss détectera automatiquement tous vos fichiers de mapping dans votre fichier HAR.
descripteur de déploiement. Si vous décidez d'utiliser le déploiement optionnel sous forme
de HAR, JBoss détectera automatiquement tous vos fichiers de mapping dans votre fichier HAR.
</para>
</listitem>
</itemizedlist>
@ -270,8 +257,8 @@
</para>
<para>
Les statistiques pendant l'exécution d'Hibernate (au runtime) sont une
autre fonctionnalité disponible en tant que service JMX. Voyez pour cela
Les statistiques pendant l'exécution d'Hibernate (au runtime) sont une
autre fonctionnalité disponible en tant que service JMX. Voyez pour cela
<xref linkend="configuration-optional-statistics"/>.
</para>
</sect1>
@ -279,46 +266,46 @@
<sect1 id="architecture-jca" revision="1">
<title>Support JCA</title>
<para>
Hibernate peut aussi être configuré en tant que connecteur JCA. Référez-vous au site
web pour de plus amples détails. Il est important de noter que le support JCA d'Hibernate
est encore considéré comme expérimental.
Hibernate peut aussi être configuré en tant que connecteur JCA. Référez-vous au site
web pour de plus amples détails. Il est important de noter que le support JCA d'Hibernate
est encore considéré comme expérimental.
</para>
</sect1>
<sect1 id="architecture-current-session" revision="1">
<title>Sessions Contextuelles</title>
<para>
Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où
une session est liée à la portée d'un contexte particulier. Cependant, les applications ne définissent
pas toutes la notion de contexte de la même manière, et différents contextes définissent différentes
portées à la notion de "courant". Les applications à base d'Hibernate, versions précédentes à la 3.0
utilisaient généralement un principe maison de sessions contextuelles basées sur le <literal>ThreadLocal</literal>,
Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où
une session est liée à la portée d'un contexte particulier. Cependant, les applications ne définissent
pas toutes la notion de contexte de la même manière, et différents contextes définissent différentes
portées à la notion de "courant". Les applications à base d'Hibernate, versions précédentes à la 3.0
utilisaient généralement un principe maison de sessions contextuelles basées sur le <literal>ThreadLocal</literal>,
ainsi que sur des classes utilitaires comme <literal>HibernateUtil</literal>, ou utilisaient des
framework tiers (comme Spring ou Pico) qui fournissaient des sessions contextuelles basées sur
framework tiers (comme Spring ou Pico) qui fournissaient des sessions contextuelles basées sur
l'utilisation de proxy/interception.
</para>
<para>
A partir de la version 3.0.1, Hibernate a ajouté la méthode <literal>SessionFactory.getCurrentSession()</literal>.
Initialement, cela demandait l'usage de transactions <literal>JTA</literal>, où la
transaction <literal>JTA</literal> définissait la portée et le contexte de la session courante.
L'équipe Hibernate pense que, étant donnée la maturité des implémentations de <literal>JTA TransactionManager</literal> ,
A partir de la version 3.0.1, Hibernate a ajouté la méthode <literal>SessionFactory.getCurrentSession()</literal>.
Initialement, cela demandait l'usage de transactions <literal>JTA</literal>, où la
transaction <literal>JTA</literal> définissait la portée et le contexte de la session courante.
L'équipe Hibernate pense que, étant donnée la maturité des implémentations de <literal>JTA TransactionManager</literal> ,
la plupart (sinon toutes) des applications devraient utiliser la gestion des transactions par <literal>JTA</literal>
qu'elles soient ou non déployées dans un conteneur <literal>J2EE</literal>. Par conséquent,
vous devriez toujours contextualiser vos sessions, si vous en avez besoin, via la méthode basée sur JTA.
qu'elles soient ou non déployées dans un conteneur <literal>J2EE</literal>. Par conséquent,
vous devriez toujours contextualiser vos sessions, si vous en avez besoin, via la méthode basée sur JTA.
</para>
<para>
Cependant, depuis la version 3.1, la logique derrière
<literal>SessionFactory.getCurrentSession()</literal> est désormais branchable.
Cependant, depuis la version 3.1, la logique derrière
<literal>SessionFactory.getCurrentSession()</literal> est désormais branchable.
A cette fin, une nouvelle interface d'extension (<literal>org.hibernate.context.CurrentSessionContext</literal>)
et un nouveau paramètre de configuration (<literal>hibernate.current_session_context_class</literal>)
ont été ajoutés pour permettre de configurer d'autres moyens de définir la portée et le contexte des
et un nouveau paramètre de configuration (<literal>hibernate.current_session_context_class</literal>)
ont été ajoutés pour permettre de configurer d'autres moyens de définir la portée et le contexte des
sessions courantes.
</para>
<para>
Allez voir les Javadocs de l'interface <literal>org.hibernate.context.CurrentSessionContext</literal>
pour une description détaillée de son contrat. Elle définit une seule méthode,
<literal>currentSession()</literal>, depuis laquelle l'implémentation est responsable
de traquer la session courante du contexte. Hibernate fournit deux implémentation
pour une description détaillée de son contrat. Elle définit une seule méthode,
<literal>currentSession()</literal>, depuis laquelle l'implémentation est responsable
de traquer la session courante du contexte. Hibernate fournit deux implémentation
de cette interface.
</para>
@ -326,38 +313,38 @@
<listitem>
<para>
<literal>org.hibernate.context.JTASessionContext</literal> - les sessions courantes sont
associées à une transaction <literal>JTA</literal>. La logique est la même que
l'ancienne approche basée sur JTA. Voir les javadocs pour les détails.
associées à une transaction <literal>JTA</literal>. La logique est la même que
l'ancienne approche basée sur JTA. Voir les javadocs pour les détails.
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.context.ThreadLocalSessionContext</literal> - les sessions
courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.
courantes sont associées au thread d'exécution. Voir les javadocs pour les détails.
</para>
</listitem>
</itemizedlist>
<para>
Les deux implémentations fournissent un modèle de programmation de type "une session - une transaction
à la base de données", aussi connu sous le nom de <emphasis>session-per-request</emphasis>.
Le début et la fin d'une session Hibernate sont définis par la durée d'une transaction de base de données.
Si vous utilisez une démarcation programmatique de la transaction (par exemple sous J2SE ou JTA/UserTransaction/BMT),
nous vous conseillons d'utiliser l'API Hibernate <literal>Transaction</literal> pour masquer le système
de transaction utilisé. Si vous exécutez sous un conteneur EJB qui supporte CMT, vous n'avez besoin d'aucune
opérations de démarcations de session ou transaction dans votre code puisque tout
est géré de manière déclarative. Référez vous à <xref linkend="transactions"/> pour plus d'informations
Les deux implémentations fournissent un modèle de programmation de type "une session - une transaction
à la base de données", aussi connu sous le nom de <emphasis>session-per-request</emphasis>.
Le début et la fin d'une session Hibernate sont définis par la durée d'une transaction de base de données.
Si vous utilisez une démarcation programmatique de la transaction (par exemple sous J2SE ou JTA/UserTransaction/BMT),
nous vous conseillons d'utiliser l'API Hibernate <literal>Transaction</literal> pour masquer le système
de transaction utilisé. Si vous exécutez sous un conteneur EJB qui supporte CMT, vous n'avez besoin d'aucune
opérations de démarcations de session ou transaction dans votre code puisque tout
est géré de manière déclarative. Référez vous à <xref linkend="transactions"/> pour plus d'informations
et des exemples de code.
</para>
<para>
Le paramètre de configuration <literal>hibernate.current_session_context_class</literal>
définit quelle implémentation de <literal>org.hibernate.context.CurrentSessionContext</literal>
doit être utilisée. Notez que pour assurer la compatibilité avec les versions précédentes, si
ce paramètre n'est pas défini mais qu'un <literal>org.hibernate.transaction.TransactionManagerLookup</literal>
est configuré, Hibernate utilisera le <literal>org.hibernate.context.JTASessionContext</literal>.
La valeur de ce paramètre devrait juste nommer la classe d'implémentation à utiliser,
pour les deux implémentations fournies, il y a cependant deux alias correspondant: "jta" et "thread".
Le paramètre de configuration <literal>hibernate.current_session_context_class</literal>
définit quelle implémentation de <literal>org.hibernate.context.CurrentSessionContext</literal>
doit être utilisée. Notez que pour assurer la compatibilité avec les versions précédentes, si
ce paramètre n'est pas défini mais qu'un <literal>org.hibernate.transaction.TransactionManagerLookup</literal>
est configuré, Hibernate utilisera le <literal>org.hibernate.context.JTASessionContext</literal>.
La valeur de ce paramètre devrait juste nommer la classe d'implémentation à utiliser,
pour les deux implémentations fournies, il y a cependant deux alias correspondant: "jta" et "thread".
</para>
</sect1>

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="associations">
<title>Mapper les associations</title>
@ -7,27 +9,27 @@
<title>Introduction</title>
<para>
Correctement mapper les associations est souvent la tâche la plus difficile.
Dans cette section nous traiterons les cas classiques les uns après les autres.
Correctement mapper les associations est souvent la tâche la plus difficile.
Dans cette section nous traiterons les cas classiques les uns après les autres.
Nous commencerons d'abbord par les mappings unidirectionnels, puis nous aborderons
la question des mappings bidirectionnels. Nous illustrerons tous nos exemples
avec les classes <literal>Person</literal> et <literal>Address</literal>.
</para>
<para>
Nous utiliserons deux critères pour classer les associations : le premier
sera de savoir si l'association est bâti sur une table supplémentaire d'association
et le deuxieme sera basé sur la multiplicité de cette association.
Nous utiliserons deux critères pour classer les associations : le premier
sera de savoir si l'association est bâti sur une table supplémentaire d'association
et le deuxieme sera basé sur la multiplicité de cette association.
</para>
<para>
Autoriser une clé étrangère nulle est considéré comme un mauvais choix dans
la construction d'un modèle de données. Nous supposerons donc que dans tous
les exemples qui vont suivre on aura interdit la valeur nulle pour les clés
étrangères. Attention, ceci ne veut pas dire que Hibernate ne supporte pas
les clés étrangères pouvant prendre des valeurs nulles, les exemples qui suivent
continueront de fonctionner si vous décidiez ne plus imposer la contrainte
de non-nullité sur les clés étrangères.
Autoriser une clé étrangère nulle est considéré comme un mauvais choix dans
la construction d'un modèle de données. Nous supposerons donc que dans tous
les exemples qui vont suivre on aura interdit la valeur nulle pour les clés
étrangères. Attention, ceci ne veut pas dire que Hibernate ne supporte pas
les clés étrangères pouvant prendre des valeurs nulles, les exemples qui suivent
continueront de fonctionner si vous décidiez ne plus imposer la contrainte
de non-nullité sur les clés étrangères.
</para>
</sect1>
@ -36,10 +38,10 @@
<title>Association unidirectionnelle</title>
<sect2 id="assoc-unidirectional-m21" >
<title>plusieurs à un</title>
<title>plusieurs à un</title>
<para>
Une <emphasis>association plusieurs-à-un (many-to-one) unidirectionnelle </emphasis>
Une <emphasis>association plusieurs-à-un (many-to-one) unidirectionnelle </emphasis>
est le type que l'on rencontre le plus souvent dans les associations unidirectionnelles.
</para>
@ -66,12 +68,12 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-unidirectional-121">
<title>un à un</title>
<title>un à un</title>
<para>
une <emphasis>association un-à-un (one-to-one) sur une clé étrangère</emphasis>
est presque identique. La seule différence est sur la contrainte d'unicité que
l'on impose à cette colonne.
une <emphasis>association un-à-un (one-to-one) sur une clé étrangère</emphasis>
est presque identique. La seule différence est sur la contrainte d'unicité que
l'on impose à cette colonne.
</para>
<programlisting><![CDATA[<class name="Person">
@ -95,8 +97,8 @@ create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
Une <emphasis>association un-à-un (one-to-one) unidirectionnelle sur une clé primaire</emphasis>
utilise un générateur d'identifiant particulier. (Remarquez que nous avons inversé le sens de cette
Une <emphasis>association un-à-un (one-to-one) unidirectionnelle sur une clé primaire</emphasis>
utilise un générateur d'identifiant particulier. (Remarquez que nous avons inversé le sens de cette
association dans cet exemple.)
</para>
@ -122,11 +124,11 @@ create table Address ( personId bigint not null primary key )
</sect2>
<sect2 id="assoc-unidirectional-12m">
<title>un à plusieurs</title>
<title>un à plusieurs</title>
<para>
Une <emphasis>association un-à-plusieurs (one-to-many) unidirectionnelle sur une
clé étrangère</emphasis> est vraiment inhabituelle, et n'est pas vraiment recommandée.
Une <emphasis>association un-à-plusieurs (one-to-many) unidirectionnelle sur une
clé étrangère</emphasis> est vraiment inhabituelle, et n'est pas vraiment recommandée.
</para>
<programlisting><![CDATA[<class name="Person">
@ -151,7 +153,7 @@ create table Address ( addressId bigint not null primary key, personId bigint no
]]></programlisting>
<para>
Nous pensons qu'il est préférable d'utiliser une table de jointure pour ce type d'association.
Nous pensons qu'il est préférable d'utiliser une table de jointure pour ce type d'association.
</para>
</sect2>
@ -162,14 +164,14 @@ create table Address ( addressId bigint not null primary key, personId bigint no
<title>Associations unidirectionnelles avec tables de jointure</title>
<sect2 id="assoc-unidirectional-join-12m">
<title>un à plusieurs</title>
<title>un à plusieurs</title>
<para>
Une <emphasis>association unidirectionnelle un-à-plusieurs (one-to-many) avec
Une <emphasis>association unidirectionnelle un-à-plusieurs (one-to-many) avec
une table de jointure</emphasis> est un bien meilleur choix.
Remarquez qu'en spécifiant <literal>unique="true"</literal>,
on a changé la multiplicité plusieurs-à-plusieurs (many-to-many) pour
un-à-plusieurs (one-to-many).
Remarquez qu'en spécifiant <literal>unique="true"</literal>,
on a changé la multiplicité plusieurs-à-plusieurs (many-to-many) pour
un-à-plusieurs (one-to-many).
</para>
<programlisting><![CDATA[<class name="Person">
@ -198,11 +200,11 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-unidirectional-join-m21">
<title>plusieurs à un</title>
<title>plusieurs à un</title>
<para>
Une <emphasis>assiociation plusieurs-à-un (many-to-one) unidirectionnelle sur
une table de jointure</emphasis> est très fréquente quand l'association est optionnelle.
Une <emphasis>assiociation plusieurs-à-un (many-to-one) unidirectionnelle sur
une table de jointure</emphasis> est très fréquente quand l'association est optionnelle.
</para>
<programlisting><![CDATA[<class name="Person">
@ -232,11 +234,11 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-unidirectional-join-121">
<title>un à un</title>
<title>un à un</title>
<para>
Une <emphasis>association unidirectionnelle un-à-un (one-to-one) sur une table
de jointure</emphasis> est extrèmement rare mais envisageable.
Une <emphasis>association unidirectionnelle un-à-un (one-to-one) sur une table
de jointure</emphasis> est extrèmement rare mais envisageable.
</para>
<programlisting><![CDATA[<class name="Person">
@ -268,10 +270,10 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-unidirectional-join-m2m">
<title>plusieurs à plusieurs</title>
<title>plusieurs à plusieurs</title>
<para>
Finallement, nous avons <emphasis>l'association unidirectionnelle plusieurs-à-plusieurs (many-to-many)</emphasis>.
Finallement, nous avons <emphasis>l'association unidirectionnelle plusieurs-à-plusieurs (many-to-many)</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
@ -304,11 +306,11 @@ create table Address ( addressId bigint not null primary key )
<title>Associations bidirectionnelles</title>
<sect2 id="assoc-bidirectional-m21" revision="2">
<title>un à plusieurs / plusieurs à un</title>
<title>un à plusieurs / plusieurs à un</title>
<para>
Une <emphasis>association bidirectionnelle plusieurs à un (many-to-one)</emphasis>
est le type d'association que l'on rencontre le plus souvent. (c'est la façon standard de créer
Une <emphasis>association bidirectionnelle plusieurs à un (many-to-one)</emphasis>
est le type d'association que l'on rencontre le plus souvent. (c'est la façon standard de créer
des relations parents/enfants.)
</para>
@ -337,10 +339,10 @@ create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
Si vous utilisez une <literal>List</literal> (ou toute autre collection indexée) vous devez
paramétrer la colonne <literal>key</literal> de la clé étrangère à <literal>not null</literal>,
et laisser Hibernate gérer l'association depuis l'extrémité collection pour maintenir l'index
de chaque élément (rendant l'autre extrémité virtuellement inverse en paramétrant
Si vous utilisez une <literal>List</literal> (ou toute autre collection indexée) vous devez
paramétrer la colonne <literal>key</literal> de la clé étrangère à <literal>not null</literal>,
et laisser Hibernate gérer l'association depuis l'extrémité collection pour maintenir l'index
de chaque élément (rendant l'autre extrémité virtuellement inverse en paramétrant
<literal>update="false"</literal> et <literal>insert="false"</literal>):
</para>
@ -367,11 +369,11 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-bidirectional-121">
<title>Un à un</title>
<title>Un à un</title>
<para>
Une <emphasis>association bidirectionnelle un à un (one-to-one) sur une clé étrangère</emphasis>
est aussi très fréquente.
Une <emphasis>association bidirectionnelle un à un (one-to-one) sur une clé étrangère</emphasis>
est aussi très fréquente.
</para>
<programlisting><![CDATA[<class name="Person">
@ -397,8 +399,8 @@ create table Address ( addressId bigint not null primary key )
]]></programlisting>
<para>
Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une clé primaire</emphasis>
utilise un générateur particulier d'id.
Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une clé primaire</emphasis>
utilise un générateur particulier d'id.
</para>
<programlisting><![CDATA[<class name="Person">
@ -430,11 +432,11 @@ create table Address ( personId bigint not null primary key )
<title>Associations bidirectionnelles avec table de jointure</title>
<sect2 id="assoc-bidirectional-join-12m">
<title>un à plusieurs / plusieurs à un</title>
<title>un à plusieurs / plusieurs à un</title>
<para>
Une <emphasis>association bidirectionnelle un-à-plusieurs (one-to-many) sur une table de jointure </emphasis>.
Remarquez que <literal>inverse="true"</literal> peut s'appliquer sur les deux extrémités de l'
Une <emphasis>association bidirectionnelle un-à-plusieurs (one-to-many) sur une table de jointure </emphasis>.
Remarquez que <literal>inverse="true"</literal> peut s'appliquer sur les deux extrémités de l'
association, sur la collection, ou sur la jointure.
</para>
@ -473,11 +475,11 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-bidirectional-join-121">
<title>Un à un</title>
<title>Un à un</title>
<para>
Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une table de jointure</emphasis>
est extrèmement rare mais envisageable.
Une <emphasis>association bidirectionnelle un-à-un (one-to-one) sur une table de jointure</emphasis>
est extrèmement rare mais envisageable.
</para>
<programlisting><![CDATA[<class name="Person">
@ -519,10 +521,10 @@ create table Address ( addressId bigint not null primary key )
</sect2>
<sect2 id="assoc-bidirectional-join-m2m" revision="1">
<title>plusieurs à plusieurs</title>
<title>plusieurs à plusieurs</title>
<para>
Finallement nous avons <emphasis>l'association bidirectionnelle plusieurs à plusieurs</emphasis>.
Finallement nous avons <emphasis>l'association bidirectionnelle plusieurs à plusieurs</emphasis>.
</para>
<programlisting><![CDATA[<class name="Person">
@ -561,12 +563,12 @@ create table Address ( addressId bigint not null primary key )
<title>Des mappings plus complexes</title>
<para>
Des associations encore plus complexes sont <emphasis>extrêmement</emphasis> rares.
Hibernate permet de gérer des situations plus complexes en utilisant des
Des associations encore plus complexes sont <emphasis>extrêmement</emphasis> rares.
Hibernate permet de gérer des situations plus complexes en utilisant des
parties SQL dans les fichiers de mapping. Par exemple, si une table
avec l'historiques des informations d'un compte définit les colonnes
avec l'historiques des informations d'un compte définit les colonnes
<literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
et <literal>effectiveStartDate</literal>, mappées de telle sorte:
et <literal>effectiveStartDate</literal>, mappées de telle sorte:
</para>
<programlisting><![CDATA[<properties name="currentAccountKey">
@ -579,7 +581,7 @@ create table Address ( addressId bigint not null primary key )
<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
<para>
alors nous pouvons mapper une association à l'instance <emphasis>courante</emphasis>
alors nous pouvons mapper une association à l'instance <emphasis>courante</emphasis>
(celle avec une <literal>effectiveEndDate</literal>) nulle en utilisant:
</para>
@ -592,10 +594,10 @@ create table Address ( addressId bigint not null primary key )
<para>
Dans un exemple plus complexe, imaginez qu'une association entre
<literal>Employee</literal> et <literal>Organization</literal> est gérée
dans une table <literal>Employment</literal> pleines de données historiques.
Dans ce cas, une association vers l'employeur <emphasis>le plus récent</emphasis>
(celui avec la <literal>startDate</literal> la plus récente) pourrait être mappée comme cela:
<literal>Employee</literal> et <literal>Organization</literal> est gérée
dans une table <literal>Employment</literal> pleines de données historiques.
Dans ce cas, une association vers l'employeur <emphasis>le plus récent</emphasis>
(celui avec la <literal>startDate</literal> la plus récente) pourrait être mappée comme cela:
</para>
<programlisting><![CDATA[<join>
@ -612,8 +614,8 @@ create table Address ( addressId bigint not null primary key )
</join>]]></programlisting>
<para>
Vous pouvez être créatif grace à ces possibilités, mais il est généralement plus pratique
d'utiliser des requêtes HQL ou criteria dans ce genre de situation.
Vous pouvez être créatif grace à ces possibilités, mais il est généralement plus pratique
d'utiliser des requêtes HQL ou criteria dans ce genre de situation.
</para>
</sect1>

View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="batch">
<title>Traitement par paquet</title>
<para>
Une approche naïve pour insérer 100 000 lignes dans la base de données en utilisant
Hibernate pourrait ressembler à ça :
Une approche naïve pour insérer 100 000 lignes dans la base de données en utilisant
Hibernate pourrait ressembler à ça :
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
@ -17,14 +19,14 @@ tx.commit();
session.close();]]></programlisting>
<para>
Ceci devrait s'écrouler avec une <literal>OutOfMemoryException</literal> quelque
part aux alentours de la 50 000ème ligne. C'est parce qu'Hibernate cache toutes
les instances de <literal>Customer</literal> nouvellement insérées dans le cache
Ceci devrait s'écrouler avec une <literal>OutOfMemoryException</literal> quelque
part aux alentours de la 50 000ème ligne. C'est parce qu'Hibernate cache toutes
les instances de <literal>Customer</literal> nouvellement insérées dans le cache
de second niveau.
</para>
<para>
Dans ce chapitre nous montrerons comment éviter ce problème. D'abord, cependant,
Dans ce chapitre nous montrerons comment éviter ce problème. D'abord, cependant,
si vous faites des traitements par batch, il est absolument critique que vous
activiez l'utilisation ds paquet JDBC (NdT : JDBC batching), si vous avez l'intention
d'obtenir des performances raisonnables. Configurez la taille du paquet JDBC avec un
@ -34,8 +36,8 @@ session.close();]]></programlisting>
<programlisting><![CDATA[hibernate.jdbc.batch_size 20]]></programlisting>
<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é :
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é :
</para>
<programlisting><![CDATA[hibernate.cache.use_second_level_cache false]]></programlisting>
@ -44,9 +46,9 @@ session.close();]]></programlisting>
<title>Insertions en paquet</title>
<para>
Lorsque vous rendez des nouveaux objets persistants, vous devez régulièrement appeler
Lorsque vous rendez des nouveaux objets persistants, vous devez régulièrement appeler
<literal>flush()</literal> et puis <literal>clear()</literal> sur la session,
pour contrôler la taille du cache de premier niveau.
pour contrôler la taille du cache de premier niveau.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
@ -55,8 +57,8 @@ Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, même taille que la taille du paquet JDBC
//flush un paquet d'insertions et libère la mémoire :
if ( i % 20 == 0 ) { //20, même taille que la taille du paquet JDBC
//flush un paquet d'insertions et libère la mémoire :
session.flush();
session.clear();
}
@ -68,12 +70,12 @@ session.close();]]></programlisting>
</sect1>
<sect1 id="batch-update" >
<title>Paquet de mises à jour</title>
<title>Paquet de mises à jour</title>
<para>
Pour récupérer et mettre à jour des données les mêmes idées s'appliquent. En plus,
Pour récupérer et mettre à jour des données les mêmes idées s'appliquent. En plus,
vous avez besoin d'utiliser <literal>scroll()</literal> pour tirer partie des
curseurs côté serveur pour les requêtes qui retournent beaucoup de lignes de données.
curseurs côté serveur pour les requêtes qui retournent beaucoup de lignes de données.
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();
@ -87,7 +89,7 @@ while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//flush un paquet de mises à jour et libère la mémoire :
//flush un paquet de mises à jour et libère la mémoire :
session.flush();
session.clear();
}
@ -101,20 +103,20 @@ session.close();]]></programlisting>
<sect1 id="batch-statelesssession">
<title>L'interface StatelessSession</title>
<para>
Alternativement, Hibernate fournit une API orientée commande qui peut être
utilisée avec des flux de données pour et en provenance de la base de données
sous la forme d'objets détachés. Une <literal>StatelessSession</literal> n'a pas
de contexte de persistance associé et ne fournit pas beaucoup de sémantique de
durée de vie de haut niveau. En particulier, une session sans état n'implémente
Alternativement, Hibernate fournit une API orientée commande qui peut être
utilisée avec des flux de données pour et en provenance de la base de données
sous la forme d'objets détachés. Une <literal>StatelessSession</literal> n'a pas
de contexte de persistance associé et ne fournit pas beaucoup de sémantique de
durée de vie de haut niveau. En particulier, une session sans état n'implémente
pas de cache de premier niveau et n'interagit pas non plus avec un cache de
seconde niveau ou un cache de requêtes. Elle n'implémente pas les transactions
ou la vérification sale automatique (NdT : automatic dirty checking). Les
opérations réalisées avec une session sans état ne sont jamais répercutées
en cascade sur les instances associées. Les collections sont ignorées par une
session sans état. Les opérations exécutées via une session sans état outrepasse
le modèle d'événements d'Hibernate et les intercepteurs. Les sessions sans état sont
vulnérables aux effets de modification des données, ceci est dû au manque de cache
de premier niveau. Une session sans état est une abstraction bas niveau, plus
seconde niveau ou un cache de requêtes. Elle n'implémente pas les transactions
ou la vérification sale automatique (NdT : automatic dirty checking). Les
opérations réalisées avec une session sans état ne sont jamais répercutées
en cascade sur les instances associées. Les collections sont ignorées par une
session sans état. Les opérations exécutées via une session sans état outrepasse
le modèle d'événements d'Hibernate et les intercepteurs. Les sessions sans état sont
vulnérables aux effets de modification des données, ceci est dû au manque de cache
de premier niveau. Une session sans état est une abstraction bas niveau, plus
proche de la couche JDBC sous-jacente.
</para>
@ -134,39 +136,39 @@ session.close();]]></programlisting>
<para>
Notez que dans le code de l'exemple, les intances de <literal>Customer</literal>
retournées par la requête sont immédiatement détachées. Elles ne sont jamais
associées à un contexte de persistance.
retournées par la requête sont immédiatement détachées. Elles ne sont jamais
associées à un contexte de persistance.
</para>
<para>
Les opérations <literal>insert()</literal>, <literal>update()</literal> et
<literal>delete()</literal> définies par l'interface <literal>StatelessSession</literal>
sont considérées comme des opérations d'accès direct aux lignes de la base de données,
ce qui résulte en une exécution immédiate du SQL <literal>INSERT</literal>, <literal>UPDATE</literal>
ou <literal>DELETE</literal> respectif. De là, elles ont des sémantiques tres différentes des
opérations <literal>save()</literal>, <literal>saveOrUpdate()</literal>
et <literal>delete()</literal> définies par l'interface <literal>Session</literal>.
Les opérations <literal>insert()</literal>, <literal>update()</literal> et
<literal>delete()</literal> définies par l'interface <literal>StatelessSession</literal>
sont considérées comme des opérations d'accès direct aux lignes de la base de données,
ce qui résulte en une exécution immédiate du SQL <literal>INSERT</literal>, <literal>UPDATE</literal>
ou <literal>DELETE</literal> respectif. De là, elles ont des sémantiques tres différentes des
opérations <literal>save()</literal>, <literal>saveOrUpdate()</literal>
et <literal>delete()</literal> définies par l'interface <literal>Session</literal>.
</para>
</sect1>
<sect1 id="batch-direct" revision="2">
<title>Opérations de style DML</title>
<title>Opérations de style DML</title>
<para>
Comme déjà discuté avant, le mapping objet/relationnel automatique et transparent
est intéressé par la gestion de l'état de l'objet. Ceci implique que l'état de l'objet
est disponible en mémoire, d'où manipuler (en utilisant des expressions du langage de
manipulation de données - <literal>Data Manipulation Language</literal> (DML) - SQL)
les données directement dans la base n'affectera pas l'état en mémoire. Pourtant, Hibernate
fournit des méthodes pour l'exécution d'expression DML de style SQL lesquelles sont
réalisées à travers le langage de requête d'Hibernate (<xref linkend="queryhql">HQL</xref>).
Comme déjà discuté avant, le mapping objet/relationnel automatique et transparent
est intéressé par la gestion de l'état de l'objet. Ceci implique que l'état de l'objet
est disponible en mémoire, d'où manipuler (en utilisant des expressions du langage de
manipulation de données - <literal>Data Manipulation Language</literal> (DML) - SQL)
les données directement dans la base n'affectera pas l'état en mémoire. Pourtant, Hibernate
fournit des méthodes pour l'exécution d'expression DML de style SQL lesquelles sont
réalisées à travers le langage de requête d'Hibernate (<xref linkend="queryhql">HQL</xref>).
</para>
<para>
La pseudo-syntaxe pour les expressions <literal>UPDATE</literal> et <literal>DELETE</literal>
est : <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>.
Certains points sont à noter :
Certains points sont à noter :
</para>
<itemizedlist spacing="compact">
@ -177,18 +179,18 @@ session.close();]]></programlisting>
</listitem>
<listitem>
<para>
Il ne peut y avoir qu'une seule entité nommée dans la clause from ; elle peut
optionnellement avoir un alias. Si le nom de l'entité a un alias, alors
n'importe quelle référence de propriété doit être qualifiée en ayant un alias ;
si le nom de l'entité n'a pas d'alias, alors il est illégal pour n'importe quelle
référence de propriété d'être qualifiée.
Il ne peut y avoir qu'une seule entité nommée dans la clause from ; elle peut
optionnellement avoir un alias. Si le nom de l'entité a un alias, alors
n'importe quelle référence de propriété doit être qualifiée en ayant un alias ;
si le nom de l'entité n'a pas d'alias, alors il est illégal pour n'importe quelle
référence de propriété d'être qualifiée.
</para>
</listitem>
<listitem>
<para>
Aucune jointure (implicite ou explicite) ne peut être spécifiée dans une requête HQL.
Les sous-requêtes peuvent être utilisées dans la clause where ; les sous-requêtes,
elles-mêmes, peuvent contenir des jointures.
Aucune jointure (implicite ou explicite) ne peut être spécifiée dans une requête HQL.
Les sous-requêtes peuvent être utilisées dans la clause where ; les sous-requêtes,
elles-mêmes, peuvent contenir des jointures.
</para>
</listitem>
<listitem>
@ -199,8 +201,8 @@ session.close();]]></programlisting>
</itemizedlist>
<para>
Par exemple, pour exécuter un <literal>UPDATE</literal> HQL, utilisez la méthode
<literal>Query.executeUpdate()</literal> (la méthode est données pour ceux
Par exemple, pour exécuter un <literal>UPDATE</literal> HQL, utilisez la méthode
<literal>Query.executeUpdate()</literal> (la méthode est données pour ceux
qui sont familiers avec <literal>PreparedStatement.executeUpdate()</literal> de
JDBC) :
</para>
@ -218,7 +220,7 @@ tx.commit();
session.close();]]></programlisting>
<para>
Pour exécuter un <literal>DELETE</literal> HQL, utilisez la même méthode
Pour exécuter un <literal>DELETE</literal> HQL, utilisez la même méthode
<literal>Query.executeUpdate()</literal> :
</para>
@ -234,85 +236,85 @@ tx.commit();
session.close();]]></programlisting>
<para>
La valeur du <literal>int</literal> retourné par la méthode <literal>Query.executeUpdate()</literal>
indique le nombre d'entités affectées par l'opération. Considérez que cela peut ou pas
corréler le nombre de lignes affectés dans la base de données. Une opération HQL
pourrait entraîner l'exécution de multiples expressions SQL réelles, pour des classes
filles mappées par jointure (NdT: join-subclass), par exemple. Le nombre retourné
indique le nombre d'entités réelles affectées par l'expression. Retour à l'exemple de la
classe fille mappée par jointure, un effacement d'une des classes filles peut réellement
entraîner des suppressions pas seulement dans la table qui mappe la classe fille, mais
La valeur du <literal>int</literal> retourné par la méthode <literal>Query.executeUpdate()</literal>
indique le nombre d'entités affectées par l'opération. Considérez que cela peut ou pas
corréler le nombre de lignes affectés dans la base de données. Une opération HQL
pourrait entraîner l'exécution de multiples expressions SQL réelles, pour des classes
filles mappées par jointure (NdT: join-subclass), par exemple. Le nombre retourné
indique le nombre d'entités réelles affectées par l'expression. Retour à l'exemple de la
classe fille mappée par jointure, un effacement d'une des classes filles peut réellement
entraîner des suppressions pas seulement dans la table qui mappe la classe fille, mais
aussi dans la table "racine" et potentillement dans les tables des classes filles plus bas
dans la hiérarchie d'héritage.
dans la hiérarchie d'héritage.
</para>
<para>
La pseudo-syntaxe pour l'expression <literal>INSERT</literal> est :
<literal>INSERT INTO EntityName properties_list select_statement</literal>. Quelques
points sont à noter :
points sont à noter :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Seule la forme INSERT INTO ... SELECT ... est supportée ; pas la forme INSERT INTO ... VALUES ... .
Seule la forme INSERT INTO ... SELECT ... est supportée ; pas la forme INSERT INTO ... VALUES ... .
</para>
<para>
La properties_list est analogue à la <literal>spécification de la colonne</literal>
La properties_list est analogue à la <literal>spécification de la colonne</literal>
The properties_list is analogous to the <literal>column speficiation</literal> dans
l'expression SQL <literal>INSERT</literal>. Pour les entités impliquées dans
un héritage mappé, seules les propriétés directement définies à ce niveau de classe
donné peuvent être utilisées dans properties_list. Les propriétés de la classe mère
ne sont pas permises ; et les propriétés des classes filles n'ont pas de sens. En
l'expression SQL <literal>INSERT</literal>. Pour les entités impliquées dans
un héritage mappé, seules les propriétés directement définies à ce niveau de classe
donné peuvent être utilisées dans properties_list. Les propriétés de la classe mère
ne sont pas permises ; et les propriétés des classes filles n'ont pas de sens. En
d'autres mots, les expressions <literal>INSERT</literal> par nature non polymorphiques.
</para>
</listitem>
<listitem>
<para>
select_statement peut être n'importe quelle requête de sélection HQl valide, avec
select_statement peut être n'importe quelle requête de sélection HQl valide, avec
l'avertissement que les types de retour doivent correspondre aux types attendus par
l'insertion. Actuellement, c'est vérifié durant la compilation de la requête plutôt
que la vérification soit reléguée à la base de données. Notez cependant que cela
pourrait poser des problèmes entre les <literal>Type</literal>s d'Hibernate qui
sont <emphasis>équivalents</emphasis> opposé à <emphasis>égaux</emphasis>. Cela
pourrait poser des problèmes avec des disparités entre une propriété définie
comme un <literal>org.hibernate.type.DateType</literal> et une propriété définie
comme un <literal>org.hibernate.type.TimestampType</literal>, même si la base de données
ne ferait pas de distinction ou ne serait pas capable de gérer la conversion.
l'insertion. Actuellement, c'est vérifié durant la compilation de la requête plutôt
que la vérification soit reléguée à la base de données. Notez cependant que cela
pourrait poser des problèmes entre les <literal>Type</literal>s d'Hibernate qui
sont <emphasis>équivalents</emphasis> opposé à <emphasis>égaux</emphasis>. Cela
pourrait poser des problèmes avec des disparités entre une propriété définie
comme un <literal>org.hibernate.type.DateType</literal> et une propriété définie
comme un <literal>org.hibernate.type.TimestampType</literal>, même si la base de données
ne ferait pas de distinction ou ne serait pas capable de gérer la conversion.
</para>
</listitem>
<listitem>
<para>
Pour la propriéte id, l'expression d'insertion vous donne deux options. Vous
pouvez soit spécifier explicitement la propriété id dans properties_list
(auquel cas sa valeur est extraite de l'expression de sélection correspondante),
soit l'omettre de properties_list (auquel cas une valeur générée est utilisée).
Cette dernière option est seulement disponible en utilisant le générateur d'identifiant
qui opère dans la base de données ; tenter d'utiliser cette option avec n'importe quel
type de générateur "en mémoire" causera une exception durant l'analyse. Notez
que pour les buts de cette discussion, les générateurs "en base" sont considérés
être <literal>org.hibernate.id.SequenceGenerator</literal> (et ses classes filles)
et n'importe quelles implémentations de
Pour la propriéte id, l'expression d'insertion vous donne deux options. Vous
pouvez soit spécifier explicitement la propriété id dans properties_list
(auquel cas sa valeur est extraite de l'expression de sélection correspondante),
soit l'omettre de properties_list (auquel cas une valeur générée est utilisée).
Cette dernière option est seulement disponible en utilisant le générateur d'identifiant
qui opère dans la base de données ; tenter d'utiliser cette option avec n'importe quel
type de générateur "en mémoire" causera une exception durant l'analyse. Notez
que pour les buts de cette discussion, les générateurs "en base" sont considérés
être <literal>org.hibernate.id.SequenceGenerator</literal> (et ses classes filles)
et n'importe quelles implémentations de
<literal>org.hibernate.id.PostInsertIdentifierGenerator</literal>.
L'exception la plus notable ici est <literal>org.hibernate.id.TableHiLoGenerator</literal>,
qu ne peut pas être utilisée parce qu'il ne propose pas un moyen de d'exposer ses valeurs
qu ne peut pas être utilisée parce qu'il ne propose pas un moyen de d'exposer ses valeurs
par un select.
</para>
</listitem>
<listitem>
<para>
Pour des propriétés mappées comme <literal>version</literal> ou <literal>timestamp</literal>,
l'expression d'insertion vous donne deux options. Vous pouvez soit spécifier la propriété dans
Pour des propriétés mappées comme <literal>version</literal> ou <literal>timestamp</literal>,
l'expression d'insertion vous donne deux options. Vous pouvez soit spécifier la propriété dans
properties_list (auquel cas sa valeur est extraite des expressions select correspondantes),
soit l'omettre de properties_list (auquel cas la <literal>valeur de graine</literal>
(NdT : seed value) définie par le <literal>org.hibernate.type.VersionType</literal> est utilisée).
(NdT : seed value) définie par le <literal>org.hibernate.type.VersionType</literal> est utilisée).
</para>
</listitem>
</itemizedlist>
<para>
Un exemple d'exécution d'une expression <literal>INSERT</literal> HQL :
Un exemple d'exécution d'une expression <literal>INSERT</literal> HQL :
</para>
<programlisting><![CDATA[Session session = sessionFactory.openSession();

View File

@ -1,25 +1,27 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="best-practices" revision="3">
<title>Meilleures pratiques</title>
<variablelist spacing="compact">
<varlistentry>
<term>Découpez finement vos classes et mappez les en utilisant <literal>&lt;component&gt;</literal>.</term>
<term>Découpez finement vos classes et mappez les en utilisant <literal>&lt;component&gt;</literal>.</term>
<listitem>
<para>
Utilisez une classe <literal>Adresse</literal> pour encapsuler <literal>Rue</literal>,
<literal>Region</literal>, <literal>CodePostal</literal>.
Ceci permet la réutilisation du code et simplifie la maintenance.
Ceci permet la réutilisation du code et simplifie la maintenance.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Déclarez des propriétés d'identifiants dans les classes persistantes.</term>
<term>Déclarez des propriétés d'identifiants dans les classes persistantes.</term>
<listitem>
<para>
Hibernate rend les propriétés d'identifiants optionnelles. Il existe beaucoup de raisons
Hibernate rend les propriétés d'identifiants optionnelles. Il existe beaucoup de raisons
pour lesquelles vous devriez les utiliser. Nous recommandons que vous utilisiez des identifiants
techniques (générés, et sans connotation métier).
techniques (générés, et sans connotation métier).
</para>
</listitem>
</varlistentry>
@ -27,9 +29,9 @@
<term>Identifiez les clefs naturelles.</term>
<listitem>
<para>
Identifiez les clefs naturelles pour toutes les entités, et mappez les avec
<literal>&lt;natural-id&gt;</literal>. Implémentez <literal>equals()</literal> et
<literal>hashCode()</literal> pour comparer les propriétés qui composent la clef naturelle.
Identifiez les clefs naturelles pour toutes les entités, et mappez les avec
<literal>&lt;natural-id&gt;</literal>. Implémentez <literal>equals()</literal> et
<literal>hashCode()</literal> pour comparer les propriétés qui composent la clef naturelle.
</para>
</listitem>
</varlistentry>
@ -39,7 +41,7 @@
<para>
N'utilisez pas un unique document de mapping. Mappez <literal>com.eg.Foo</literal> dans
le fichier <literal>com/eg/Foo.hbm.xml</literal>. Cela prend tout son sens lors
d'un travail en équipe.
d'un travail en équipe.
</para>
</listitem>
</varlistentry>
@ -47,63 +49,63 @@
<term>Chargez les mappings comme des ressources.</term>
<listitem>
<para>
Déployez les mappings en même temps que les classes qu'ils mappent.
Déployez les mappings en même temps que les classes qu'ils mappent.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pensez à externaliser les chaînes de caractères.</term>
<term>Pensez à externaliser les chaînes de caractères.</term>
<listitem>
<para>
Ceci est une bonne habitude si vos requêtes appellent des fonctions SQL qui ne sont
Ceci est une bonne habitude si vos requêtes appellent des fonctions SQL qui ne sont
pas au standard ANSI. Cette externalisation dans les fichiers de mapping rendra votre
application plus portable.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Utilisez les variables "bindées".</term>
<term>Utilisez les variables "bindées".</term>
<listitem>
<para>
Comme en JDBC, remplacez toujours les valeurs non constantes par "?". N'utilisez jamais
la manipulation des chaînes de caractères pour remplacer des valeurs non constantes dans
une requête ! Encore mieux, utilisez les paramètres nommés dans les requêtes.
la manipulation des chaînes de caractères pour remplacer des valeurs non constantes dans
une requête ! Encore mieux, utilisez les paramètres nommés dans les requêtes.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Ne gérez pas vous même les connexions JDBC.</term>
<term>Ne gérez pas vous même les connexions JDBC.</term>
<listitem>
<para>
Hibernate laisse l'application gérer les connexions JDBC. Vous ne devriez gérer vos connexions
qu'en dernier recours. Si vous ne pouvez pas utiliser les systèmes de connexions livrés,
réfléchissez à l'idée de fournir votre propre implémentation de <literal>org.hibernate.connection.ConnectionProvider</literal>.
Hibernate laisse l'application gérer les connexions JDBC. Vous ne devriez gérer vos connexions
qu'en dernier recours. Si vous ne pouvez pas utiliser les systèmes de connexions livrés,
réfléchissez à l'idée de fournir votre propre implémentation de <literal>org.hibernate.connection.ConnectionProvider</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pensez à utiliser les types utilisateurs.</term>
<term>Pensez à utiliser les types utilisateurs.</term>
<listitem>
<para>
Supposez que vous ayez une type Java, de telle bibliothèque, qui a besoin d'être persisté mais
qui ne fournit pas les accesseurs nécessaires pour le mapper comme composant. Vous devriez
implémenter
<literal>org.hibernate.UserType</literal>.Cette approche libère le code de l'application
de l'implémentation des transformations vers / depuis les types Hibernate.
Supposez que vous ayez une type Java, de telle bibliothèque, qui a besoin d'être persisté mais
qui ne fournit pas les accesseurs nécessaires pour le mapper comme composant. Vous devriez
implémenter
<literal>org.hibernate.UserType</literal>.Cette approche libère le code de l'application
de l'implémentation des transformations vers / depuis les types Hibernate.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Utilisez du JDBC pur dans les goulets d'étranglement.</term>
<term>Utilisez du JDBC pur dans les goulets d'étranglement.</term>
<listitem>
<para>
Dans certaines parties critiques de votre système d'un point de vue performance, quelques opérations
Dans certaines parties critiques de votre système d'un point de vue performance, quelques opérations
peuvent tirer partie d'un appel JDBC natif.
Mais attendez de <emphasis>savoir</emphasis>
que c'est un goulet d'étranglement. Ne supposez jamais qu'un appel JDBC sera forcément plus
que c'est un goulet d'étranglement. Ne supposez jamais qu'un appel JDBC sera forcément plus
rapide. Si vous avez besoin d'utiliser JDBC directement, ouvrez une <literal>Session</literal>
Hibernate et utilisez la connexion SQL sous-jacente. Ainsi vous pourrez utiliser la même stratégie
de transation et la même gestion des connexions.
Hibernate et utilisez la connexion SQL sous-jacente. Ainsi vous pourrez utiliser la même stratégie
de transation et la même gestion des connexions.
</para>
</listitem>
</varlistentry>
@ -111,101 +113,101 @@
<term>Comprendre le flush de <literal>Session</literal>.</term>
<listitem>
<para>
De temps en temps la Session synchronise ses états persistants avec la base de données.
Les performances seront affectées si ce processus arrive trop souvent. Vous pouvez parfois
minimiser les flush non nécessaires en désactivant le flush automatique ou même en changeant
l'ordre des opérations menées dans une transaction particulière.
De temps en temps la Session synchronise ses états persistants avec la base de données.
Les performances seront affectées si ce processus arrive trop souvent. Vous pouvez parfois
minimiser les flush non nécessaires en désactivant le flush automatique ou même en changeant
l'ordre des opérations menées dans une transaction particulière.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Dans une architecture à trois couches, pensez à utiliser <literal>saveOrUpdate()</literal>.</term>
<term>Dans une architecture à trois couches, pensez à utiliser <literal>saveOrUpdate()</literal>.</term>
<listitem>
<para>
Quand vous utilisez une architecture à base de servlet / session bean, vous pourriez passer
des objets chargés dans le bean session vers et depuis la couche servlet / JSP. Utilisez
une nouvelle session pour traiter chaque requête.
Quand vous utilisez une architecture à base de servlet / session bean, vous pourriez passer
des objets chargés dans le bean session vers et depuis la couche servlet / JSP. Utilisez
une nouvelle session pour traiter chaque requête.
Utilisez <literal>Session.merge()</literal> ou <literal>Session.saveOrUpdate()</literal> pour
synchroniser les objets avec la base de données.
synchroniser les objets avec la base de données.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Dans une architecture à deux couches, pensez à utiliser la déconnexion de session.</term>
<term>Dans une architecture à deux couches, pensez à utiliser la déconnexion de session.</term>
<listitem>
<para>
Les transactions de bases de données doivent être aussi courtes que possible
pour une meilleure montée en charge.Cependant, il est souvent nécessaire d'implémenter
de longues <emphasis>transactions applicatives</emphasis>, une simple unité de travail du point de vue de
Les transactions de bases de données doivent être aussi courtes que possible
pour une meilleure montée en charge.Cependant, il est souvent nécessaire d'implémenter
de longues <emphasis>transactions applicatives</emphasis>, une simple unité de travail du point de vue de
l'utilisateur. Une transaction applicative
peut s'étaler sur plusieurs cycles de requêtes/réponses du client.
Il est commun d'utiliser des objets détachés pour implémenter des transactions applicatives.
Une alternative, extrêmement appropriée dans une architecture à 2 couches, est de
maintenir un seul contact de persistance ouvert (session) pour toute la durée de vie
de la transaction applicative et simplement se déconnecter de la connexion JDBC à la fin de chaque requête,
et se reconnecter au début de la requête suivante. Ne partagez jamais une seule
peut s'étaler sur plusieurs cycles de requêtes/réponses du client.
Il est commun d'utiliser des objets détachés pour implémenter des transactions applicatives.
Une alternative, extrêmement appropriée dans une architecture à 2 couches, est de
maintenir un seul contact de persistance ouvert (session) pour toute la durée de vie
de la transaction applicative et simplement se déconnecter de la connexion JDBC à la fin de chaque requête,
et se reconnecter au début de la requête suivante. Ne partagez jamais une seule
session avec plus d'une transaction applicative, ou vous travaillerez avec des
données périmées.
données périmées.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Considérez que les exceptions ne sont pas rattrapables.</term>
<term>Considérez que les exceptions ne sont pas rattrapables.</term>
<listitem>
<para>
Il s'agit plus d'une pratique obligatoire que d'une "meilleure pratique". Quand une exception
intervient, il faut faire un rollback de la <literal>Transaction</literal> et
fermer la <literal>Session</literal>.
Sinon, Hibernate ne peut garantir l'intégrité des états persistants en mémoire. En particulier,
n'utilisez pas <literal>Session.load()</literal> pour déterminer si une instance avec un identifiant
donné existe en base de données, utilisez <literal>Session.get()</literal> ou un requête.
Sinon, Hibernate ne peut garantir l'intégrité des états persistants en mémoire. En particulier,
n'utilisez pas <literal>Session.load()</literal> pour déterminer si une instance avec un identifiant
donné existe en base de données, utilisez <literal>Session.get()</literal> ou un requête.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Préférez le chargement tardif des associations.</term>
<term>Préférez le chargement tardif des associations.</term>
<listitem>
<para>
Utilisez le chargement complet avec modération.
Utilisez les proxies et les collections chargées tardivement
Utilisez le chargement complet avec modération.
Utilisez les proxies et les collections chargées tardivement
pour la plupart des associations vers des classes qui ne sont pas susceptibles
d'être complètement retenues dans le cache de second niveau.
Pour les assocations de classes en cache, où il y a une extrêmement
forte probabilité que l'élément soit en cache, désactivez explicitement le chargement
d'être complètement retenues dans le cache de second niveau.
Pour les assocations de classes en cache, où il y a une extrêmement
forte probabilité que l'élément soit en cache, désactivez explicitement le chargement
par jointures ouvertes en utilisant <literal>outer-join="false"</literal>.
Lorsqu'un chargement par jointure ouverte est approprié pour un cas d'utilisation
particulier, utilisez une requête avec un <literal>left join fetch</literal>.
Lorsqu'un chargement par jointure ouverte est approprié pour un cas d'utilisation
particulier, utilisez une requête avec un <literal>left join fetch</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
Utilisez le pattern <emphasis>d'une ouverture de session dans une vue</emphasis>,
ou une <emphasis>phase d'assemblage</emphasis> disciplinée pour éviter des problèmes
avec des données non rapatriées.
ou une <emphasis>phase d'assemblage</emphasis> disciplinée pour éviter des problèmes
avec des données non rapatriées.
</term>
<listitem>
<para>
Hibernate libère les développeurs de l'écriture fastidieuse des <emphasis>objets de transfert
de données (NdT : Data Transfer Objects)</emphasis> (DTO). Dans une architecture EJB traditionnelle,
les DTOs ont deux buts : premièrement, ils contournent le problème des "entity bean" qui ne sont pas
sérialisables ; deuxièmement, ils définissent implicitement une phase d'assemblage où toutes les
données utilisées par la vue sont rapatriées et organisées dans les DTOs avant de retourner sous le
contrôle de la couche de présentation. Hibernate élimine le premier but. Pourtant, vous aurez encore
besoin d'une phase d'assemblage (pensez vos méthodes métier comme ayant un contrat strict avec la
couche de présentation à propos de quelles données sont disponibles dans les objets détachés)
à moins que vous soyez préparés à garder le contexte de
persistance (la session) ouvert à travers tout le processus de rendu de la vue.
Hibernate libère les développeurs de l'écriture fastidieuse des <emphasis>objets de transfert
de données (NdT : Data Transfer Objects)</emphasis> (DTO). Dans une architecture EJB traditionnelle,
les DTOs ont deux buts : premièrement, ils contournent le problème des "entity bean" qui ne sont pas
sérialisables ; deuxièmement, ils définissent implicitement une phase d'assemblage où toutes les
données utilisées par la vue sont rapatriées et organisées dans les DTOs avant de retourner sous le
contrôle de la couche de présentation. Hibernate élimine le premier but. Pourtant, vous aurez encore
besoin d'une phase d'assemblage (pensez vos méthodes métier comme ayant un contrat strict avec la
couche de présentation à propos de quelles données sont disponibles dans les objets détachés)
à moins que vous soyez préparés à garder le contexte de
persistance (la session) ouvert à travers tout le processus de rendu de la vue.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pensez à abstraite votre logique métier d'Hibernate.</term>
<term>Pensez à abstraite votre logique métier d'Hibernate.</term>
<listitem>
<para>
Cachez le mécanisme d'accès aux données (Hibernate) derrière une interface. Combinez les patterns
<emphasis>DAO</emphasis> et <emphasis>Thread Local Session</emphasis>. Vous pouvez même avoir quelques
classes persistées par du JDBC pur, associées à Hibernate via un <literal>UserType</literal> (ce conseil est
Cachez le mécanisme d'accès aux données (Hibernate) derrière une interface. Combinez les patterns
<emphasis>DAO</emphasis> et <emphasis>Thread Local Session</emphasis>. Vous pouvez même avoir quelques
classes persistées par du JDBC pur, associées à Hibernate via un <literal>UserType</literal> (ce conseil est
valable pour des applications de taille respectables ; il n'est pas valable pour une application
avec cinq tables).
</para>
@ -217,20 +219,20 @@
<para>
De bons cas d'utilisation pour de vraies associations plusieurs-vers-plusieurs
sont rares. La plupart du temps vous avez besoin d'informations additionnelles
stockées dans la table d'association.
Dans ce cas, il est préférable d'utiliser deux associations un-vers-plusieurs vers une classe
de liaisons intermédiaire. En fait, nous pensons que la plupart des associations sont
de type un-vers-plusieurs ou plusieurs-vers-un, vous devez être très attentifs lorsque
vous utilisez autre chose et vous demander si c'est vraiment nécessaire.
stockées dans la table d'association.
Dans ce cas, il est préférable d'utiliser deux associations un-vers-plusieurs vers une classe
de liaisons intermédiaire. En fait, nous pensons que la plupart des associations sont
de type un-vers-plusieurs ou plusieurs-vers-un, vous devez être très attentifs lorsque
vous utilisez autre chose et vous demander si c'est vraiment nécessaire.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Préférez les associations bidirectionnelles.</term>
<term>Préférez les associations bidirectionnelles.</term>
<listitem>
<para>
Les associations unidirectionnelles sont plus difficiles à questionner.
Dans une grande application, la plupart des associations devraient être navigables dans les deux directions dans les requêtes.
Les associations unidirectionnelles sont plus difficiles à questionner.
Dans une grande application, la plupart des associations devraient être navigables dans les deux directions dans les requêtes.
</para>
</listitem>
</varlistentry>

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="collections">
<title>Mapping des collections</title>
@ -6,7 +8,7 @@
<title>Collections persistantes</title>
<para>
Hibernate requiert que les champs contenant des collections persistantes soient déclarés
Hibernate requiert que les champs contenant des collections persistantes soient déclarés
comme des types d'interface, par exemple :
</para>
@ -21,21 +23,21 @@
}]]></programlisting>
<para>
L'interface réelle devrait être <literal>java.util.Set</literal>,
L'interface réelle devrait être <literal>java.util.Set</literal>,
<literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
<literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
<literal>java.util.SortedMap</literal> ou ... n'importe quoi d'autre ! (Où
"n'importe quoi d'autre" signifie que vous devrez écrire une implémentation de
<literal>java.util.SortedMap</literal> ou ... n'importe quoi d'autre ! (Où
"n'importe quoi d'autre" signifie que vous devrez écrire une implémentation de
<literal>org.hibernate.usertype.UserCollectionType</literal>.)
</para>
<para>
Notez comment nous avons initialisé les variables d'instance avec une instance de
Notez comment nous avons initialisé les variables d'instance avec une instance de
<literal>HashSet</literal>. C'est le meilleur moyen pour initialiser les
collections d'instances nouvellement créées (non persistantes). Quand
collections d'instances nouvellement créées (non persistantes). Quand
nous fabriquons l'instance persistante - en appelant <literal>persist()</literal>,
par exemple - Hibernate remplacera réellement le <literal>HashSet</literal>
avec une instance d'une implémentation propre à Hibernate de <literal>Set</literal>.
par exemple - Hibernate remplacera réellement le <literal>HashSet</literal>
avec une instance d'une implémentation propre à Hibernate de <literal>Set</literal>.
Prenez garde aux erreurs :
</para>
@ -50,7 +52,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
(HashSet) cat.getKittens(); // Erreur !]]></programlisting>
<para>
Les collections persistantes injectées par Hibernate se comportent de la même manière que
Les collections persistantes injectées par Hibernate se comportent de la même manière que
<literal>HashMap</literal>, <literal>HashSet</literal>,
<literal>TreeMap</literal>, <literal>TreeSet</literal> ou
<literal>ArrayList</literal>, selon le type de l'interface.
@ -58,19 +60,19 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<para>
Les instances des collections ont le comportement habituel des types des valeurs.
Elles sont automatiquement persistées quand elles sont référencées par un objet persistant et
automatiquement effacées quand elles sont déréférencées. Si une collection est passée
d'un objet persistant à un autre, ses éléments pourraient être déplacés d'une table
à une autre. Deux entités ne peuvent pas partager une référence vers une même instance
d'une collection. Dû au modèle relationnel sous-jacent, les propriétés contenant des
collections ne supportent pas la sémantique de la valeur null ; Hibernate ne distingue pas
une référence vers une collection nulle d'une collection vide.
Elles sont automatiquement persistées quand elles sont référencées par un objet persistant et
automatiquement effacées quand elles sont déréférencées. Si une collection est passée
d'un objet persistant à un autre, ses éléments pourraient être déplacés d'une table
à une autre. Deux entités ne peuvent pas partager une référence vers une même instance
d'une collection. Dû au modèle relationnel sous-jacent, les propriétés contenant des
collections ne supportent pas la sémantique de la valeur null ; Hibernate ne distingue pas
une référence vers une collection nulle d'une collection vide.
</para>
<para>
Vous ne devriez pas vous préoccuper trop de ça. Utilisez les collections persistantes de
la même manière que vous utilisez des collections Java ordinaires. Assurez-vous
de comprendre la sémantique des associations bidirectionnelles (traitée plus loin).
Vous ne devriez pas vous préoccuper trop de ça. Utilisez les collections persistantes de
la même manière que vous utilisez des collections Java ordinaires. Assurez-vous
de comprendre la sémantique des associations bidirectionnelles (traitée plus loin).
</para>
</sect1>
@ -79,9 +81,9 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<title>Mapper une collection</title>
<para>
L'élément de mapping d'Hibernate utilisé pour mapper une collection dépend du type de
l'interface. Par exemple, un élément <literal>&lt;set&gt;</literal> est utilisé
pour mapper des propriétés de type <literal>Set</literal>.
L'élément de mapping d'Hibernate utilisé pour mapper une collection dépend du type de
l'interface. Par exemple, un élément <literal>&lt;set&gt;</literal> est utilisé
pour mapper des propriétés de type <literal>Set</literal>.
</para>
<programlisting><![CDATA[<class name="Product">
@ -93,11 +95,11 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</class>]]></programlisting>
<para>
À part <literal>&lt;set&gt;</literal>, il y aussi les éléments de mapping
À part <literal>&lt;set&gt;</literal>, il y aussi les éléments de mapping
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal>, <literal>&lt;array&gt;</literal> et
<literal>&lt;primitive-array&gt;</literal>.
L'élément <literal>&lt;map&gt;</literal> est représentatif :
L'élément <literal>&lt;map&gt;</literal> est représentatif :
</para>
<programlistingco>
@ -143,97 +145,97 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<calloutlist>
<callout arearefs="mappingcollection1">
<para>
<literal>name</literal> : le nom de la propriété contenant la collection
<literal>name</literal> : le nom de la propriété contenant la collection
</para>
</callout>
<callout arearefs="mappingcollection2">
<para>
<literal>table</literal> (optionnel - par défaut = nom de la propriété) : le
nom de la table de la collection (non utilisé pour les associations one-to-many)
<literal>table</literal> (optionnel - par défaut = nom de la propriété) : le
nom de la table de la collection (non utilisé pour les associations one-to-many)
</para>
</callout>
<callout arearefs="mappingcollection3">
<para>
<literal>schema</literal> (optionnel) : le nom du schéma pour surcharger le
schéma déclaré dans l'élément racine
<literal>schema</literal> (optionnel) : le nom du schéma pour surcharger le
schéma déclaré dans l'élément racine
</para>
</callout>
<callout arearefs="mappingcollection4">
<para>
<literal>lazy</literal> (optionnel - par défaut = <literal>true</literal>) :
peut être utilisé pour désactiver l'initialisation tardive et spécifier
que l'association est toujours rapportée, ou pour activer la
récupération extra-paresseuse (NdT : extra-lazy) où la plupart des
opérations n'initialisent pas la collection (approprié pour de très
<literal>lazy</literal> (optionnel - par défaut = <literal>true</literal>) :
peut être utilisé pour désactiver l'initialisation tardive et spécifier
que l'association est toujours rapportée, ou pour activer la
récupération extra-paresseuse (NdT : extra-lazy) où la plupart des
opérations n'initialisent pas la collection (approprié pour de très
grosses collections)
</para>
</callout>
<callout arearefs="mappingcollection5">
<para>
<literal>inverse</literal> (optionnel - par défaut = <literal>false</literal>) :
définit cette collection comme l'extrêmité "inverse" de l'association
<literal>inverse</literal> (optionnel - par défaut = <literal>false</literal>) :
définit cette collection comme l'extrêmité "inverse" de l'association
bidirectionnelle
</para>
</callout>
<callout arearefs="mappingcollection6">
<para>
<literal>cascade</literal> (optionnel - par défaut = <literal>none</literal>) :
active les opérations de cascade vers les entités filles
<literal>cascade</literal> (optionnel - par défaut = <literal>none</literal>) :
active les opérations de cascade vers les entités filles
</para>
</callout>
<callout arearefs="mappingcollection7">
<para>
<literal>sort</literal> (optionnel) : spécifie une collection triée via un ordre
de tri <literal>naturel</literal>, ou via une classe comparateur donnée (implémentant Comparator)
<literal>sort</literal> (optionnel) : spécifie une collection triée via un ordre
de tri <literal>naturel</literal>, ou via une classe comparateur donnée (implémentant Comparator)
</para>
</callout>
<callout arearefs="mappingcollection8">
<para>
<literal>order-by</literal> (optionnel, seulement à partir du JDK1.4) :
spécifie une colonne de table
(ou des colonnes) qui définit l'ordre d'itération de <literal>Map</literal>, <literal>Set</literal>
<literal>order-by</literal> (optionnel, seulement à partir du JDK1.4) :
spécifie une colonne de table
(ou des colonnes) qui définit l'ordre d'itération de <literal>Map</literal>, <literal>Set</literal>
ou Bag, avec en option <literal>asc</literal> ou <literal>desc</literal>
</para>
</callout>
<callout arearefs="mappingcollection9">
<para>
<literal>where</literal> (optionnel) : spécifie une condition SQL arbitraire <literal>WHERE</literal>
à utiliser au chargement ou à la suppression d'une collection (utile si la collection
ne doit contenir qu'un sous ensemble des données disponibles)
<literal>where</literal> (optionnel) : spécifie une condition SQL arbitraire <literal>WHERE</literal>
à utiliser au chargement ou à la suppression d'une collection (utile si la collection
ne doit contenir qu'un sous ensemble des données disponibles)
</para>
</callout>
<callout arearefs="mappingcollection10">
<para>
<literal>fetch</literal> (optionnel, par défaut = <literal>select</literal>) :
à choisir entre récupération par jointures externes, récupération par
selects séquentiels, et récupération par sous-selects séquentiels
<literal>fetch</literal> (optionnel, par défaut = <literal>select</literal>) :
à choisir entre récupération par jointures externes, récupération par
selects séquentiels, et récupération par sous-selects séquentiels
</para>
</callout>
<callout arearefs="mappingcollection11">
<para>
<literal>batch-size</literal> (optionnel, par défaut = <literal>1</literal>) : une taille
de batch (batch size) utilisée pour charger plusieurs instances de cette collection en
<literal>batch-size</literal> (optionnel, par défaut = <literal>1</literal>) : une taille
de batch (batch size) utilisée pour charger plusieurs instances de cette collection en
initialisation tardive
</para>
</callout>
<callout arearefs="mappingcollection12">
<para>
<literal>access</literal> (optionnel - par défaut = <literal>property</literal>) : La
stratégie qu'Hibernate doit utiliser pour accéder à la valeur de la propriété
<literal>access</literal> (optionnel - par défaut = <literal>property</literal>) : La
stratégie qu'Hibernate doit utiliser pour accéder à la valeur de la propriété
</para>
</callout>
<callout arearefs="mappingcollection13">
<para>
<literal>optimistic-lock</literal> (optionnel - par défaut = <literal>true</literal>) :
spécifie que changer l'état de la collection entraîne l'incrémentation
de la version appartenant à l'entité (Pour une association un vers plusieurs,
il est souvent raisonnable de désactiver ce paramètre)
<literal>optimistic-lock</literal> (optionnel - par défaut = <literal>true</literal>) :
spécifie que changer l'état de la collection entraîne l'incrémentation
de la version appartenant à l'entité (Pour une association un vers plusieurs,
il est souvent raisonnable de désactiver ce paramètre)
</para>
</callout>
<callout arearefs="mappingcollection14">
<para>
<literal>mutable</literal> (optionnel - par défaut = <literal>true</literal>) :
une valeur à <literal>false</literal> spécifie que les éléments de la
<literal>mutable</literal> (optionnel - par défaut = <literal>true</literal>) :
une valeur à <literal>false</literal> spécifie que les éléments de la
collection ne changent jamais (une optimisation mineure dans certains cas)
</para>
</callout>
@ -241,74 +243,74 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</programlistingco>
<sect2 id="collections-foreignkeys" >
<title>Les clefs étrangères d'une collection</title>
<title>Les clefs étrangères d'une collection</title>
<para>
Les instances d'une collection sont distinguées dans la base par la clef étrangère
de l'entité qui possède la collection. Cette clef étrangère est référencée comme la(es)
Les instances d'une collection sont distinguées dans la base par la clef étrangère
de l'entité qui possède la collection. Cette clef étrangère est référencée comme la(es)
<emphasis>colonne(s) de la clef de la collection</emphasis> de la table de la collection.
La colonne de la clef de la collection est mappée par l'élément <literal>&lt;key&gt;</literal>.
La colonne de la clef de la collection est mappée par l'élément <literal>&lt;key&gt;</literal>.
</para>
<para>
Il peut y avoir une contrainte de nullité sur la colonne de la clef étrangère. Pour les
associations unidirectionnelles un vers plusieurs, la colonne de la clef étrangère
peut être nulle par défaut, donc vous pourriez avoir besoin de spécifier
Il peut y avoir une contrainte de nullité sur la colonne de la clef étrangère. Pour les
associations unidirectionnelles un vers plusieurs, la colonne de la clef étrangère
peut être nulle par défaut, donc vous pourriez avoir besoin de spécifier
<literal>not-null="true"</literal>.
</para>
<programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
<para>
La contraite de la clef étrangère peut utiliser <literal>ON DELETE CASCADE</literal>.
La contraite de la clef étrangère peut utiliser <literal>ON DELETE CASCADE</literal>.
</para>
<programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
<para>
Voir le chapitre précédent pour une définition complète de l'élément <literal>&lt;key&gt;</literal>.
Voir le chapitre précédent pour une définition complète de l'élément <literal>&lt;key&gt;</literal>.
</para>
</sect2>
<sect2 id="collections-elements" >
<title>Les éléments d'une collection</title>
<title>Les éléments d'une collection</title>
<para>
Les collections peuvent contenir la plupart des autres types Hibernate, dont tous les types
basiques, les types utilisateur, les composants, et bien sûr, les références vers
d'autres entités. C'est une distinction importante : un objet dans une collection
pourrait être géré avec une sémantique de "valeur" (sa durée de vie dépend complètement
du propriétaire de la collection) ou il pourrait avoir une référence vers une autre
entité, avec sa propre durée de vie. Dans le dernier cas, seul le "lien" entre les 2 objets
est considéré être l'état retenu par la collection.
basiques, les types utilisateur, les composants, et bien sûr, les références vers
d'autres entités. C'est une distinction importante : un objet dans une collection
pourrait être géré avec une sémantique de "valeur" (sa durée de vie dépend complètement
du propriétaire de la collection) ou il pourrait avoir une référence vers une autre
entité, avec sa propre durée de vie. Dans le dernier cas, seul le "lien" entre les 2 objets
est considéré être l'état retenu par la collection.
</para>
<para>
Le type contenu est référencé comme le <emphasis>type de l'élément de la collection</emphasis>.
Les éléments de la collections sont mappés par <literal>&lt;element&gt;</literal> ou
<literal>&lt;composite-element&gt;</literal>, ou dans le cas des références d'entité, avec
Le type contenu est référencé comme le <emphasis>type de l'élément de la collection</emphasis>.
Les éléments de la collections sont mappés par <literal>&lt;element&gt;</literal> ou
<literal>&lt;composite-element&gt;</literal>, ou dans le cas des références d'entité, avec
<literal>&lt;one-to-many&gt;</literal> ou <literal>&lt;many-to-many&gt;</literal>.
Les deux premiers mappent des éléments avec un sémantique de valeur, les deux suivants sont
utilisés pour mapper des associations d'entité.
Les deux premiers mappent des éléments avec un sémantique de valeur, les deux suivants sont
utilisés pour mapper des associations d'entité.
</para>
</sect2>
<sect2 id="collections-indexed">
<title>Collections indexées</title>
<title>Collections indexées</title>
<para>
Tous les mappings de collection, exceptés ceux avec les sémantiques d'ensemble (NdT : set) et
Tous les mappings de collection, exceptés ceux avec les sémantiques d'ensemble (NdT : set) et
de sac (NdT : bag), ont besoin d'une <emphasis>colonne d'index</emphasis> dans la
table de la collection - une colonne qui mappe un index de tableau, ou un index de
<literal>List</literal>, ou une clef de <literal>Map</literal>. L'index d'une
<literal>Map</literal> peut être n'importe quel type basique, mappé avec
<literal>&lt;map-key&gt;</literal>, ça peut être une référence d'entité mappée avec
<literal>&lt;map-key-many-to-many&gt;</literal>, ou ça peut être un type composé, mappé avec
<literal>Map</literal> peut être n'importe quel type basique, mappé avec
<literal>&lt;map-key&gt;</literal>, ça peut être une référence d'entité mappée avec
<literal>&lt;map-key-many-to-many&gt;</literal>, ou ça peut être un type composé, mappé avec
<literal>&lt;composite-map-key&gt;</literal>. L'index d'un tableau ou d'une liste est toujours
de type <literal>integer</literal> et est mappé en utilisant l'élément <literal>&lt;list-index&gt;</literal>.
Les colonnes mappées contiennent des entiers séquentiels (numérotés à partir de zéro par défaut).
de type <literal>integer</literal> et est mappé en utilisant l'élément <literal>&lt;list-index&gt;</literal>.
Les colonnes mappées contiennent des entiers séquentiels (numérotés à partir de zéro par défaut).
</para>
<programlistingco>
@ -327,8 +329,8 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</callout>
<callout arearefs="index1">
<para>
<literal>base</literal> (optionnel, par défaut = <literal>0</literal>) : la valeur
de la colonne de l'index qui correspond au premier élément de la liste ou du tableau
<literal>base</literal> (optionnel, par défaut = <literal>0</literal>) : la valeur
de la colonne de l'index qui correspond au premier élément de la liste ou du tableau
</para>
</callout>
</calloutlist>
@ -356,7 +358,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<callout arearefs="mapkey2">
<para>
<literal>formula</literal> (optionnel) :
une formule SQL utilisée pour évaluer la clef de la map
une formule SQL utilisée pour évaluer la clef de la map
</para>
</callout>
<callout arearefs="mapkey3">
@ -382,35 +384,35 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<callout arearefs="indexmanytomany1">
<para>
<literal>column</literal> (optionnel) :
le nom de la colonne de la clef étrangère pour les valeurs de l'index de la collection
le nom de la colonne de la clef étrangère pour les valeurs de l'index de la collection
</para>
</callout>
<callout arearefs="indexmanytomany2">
<para>
<literal>formula</literal> (optionnel) :
une formulre SQL utilisée pour évaluer la clef étrangère de la clef de la map
une formulre SQL utilisée pour évaluer la clef étrangère de la clef de la map
</para>
</callout>
<callout arearefs="indexmanytomany3">
<para>
<literal>class</literal> (requis): la classe de l'entité utilisée comme clef de la map
<literal>class</literal> (requis): la classe de l'entité utilisée comme clef de la map
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Si votre table n'a pas de colonne d'index, et que vous souhaitez tout de même utiliser
<literal>List</literal> comme type de propriété, vous devriez mapper la propriété comme un
Si votre table n'a pas de colonne d'index, et que vous souhaitez tout de même utiliser
<literal>List</literal> comme type de propriété, vous devriez mapper la propriété comme un
<emphasis>&lt;bag&gt;</emphasis> Hibernate. Un sac (NdT : bag) ne garde pas son ordre quand
il est récupéré de la base de données, mais il peut être optionnellement trié ou ordonné.
il est récupéré de la base de données, mais il peut être optionnellement trié ou ordonné.
</para>
</sect2>
<para>
Il y a pas mal de variétés de mappings qui peuvent être générés pour les collections,
couvrant beaucoup des modèles relationnels communs. Nous vous suggérons d'expérimenter avec l'outil de
génération de schéma pour avoir une idée de comment traduire les différentes déclarations de mapping vers des table de la base de données.
Il y a pas mal de variétés de mappings qui peuvent être générés pour les collections,
couvrant beaucoup des modèles relationnels communs. Nous vous suggérons d'expérimenter avec l'outil de
génération de schéma pour avoir une idée de comment traduire les différentes déclarations de mapping vers des table de la base de données.
</para>
<sect2 id="collections-ofvalues" revision="2">
@ -418,8 +420,8 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<para>
N'importe quelle collection de valeurs ou association plusieurs-vers-plusieurs requiert une
<emphasis>table de collection</emphasis> avec une(des) colonne(s) de clef étrangère, une(des)
<emphasis>colonne(s) d'élément de la collection</emphasis> ou des colonnes et possiblement
<emphasis>table de collection</emphasis> avec une(des) colonne(s) de clef étrangère, une(des)
<emphasis>colonne(s) d'élément de la collection</emphasis> ou des colonnes et possiblement
une(des) colonne(s) d'index.
</para>
@ -447,25 +449,25 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<calloutlist>
<callout arearefs="element1b">
<para>
<literal>column</literal> (optionnel) : le nom de la colonne contenant les valeurs de l'élément de la collection
<literal>column</literal> (optionnel) : le nom de la colonne contenant les valeurs de l'élément de la collection
</para>
</callout>
<callout arearefs="element2b">
<para>
<literal>formula</literal> (optionnel) : une formule SQL utilisée pour évaluer l'élément
<literal>formula</literal> (optionnel) : une formule SQL utilisée pour évaluer l'élément
</para>
</callout>
<callout arearefs="element3b">
<para>
<literal>type</literal> (requis) : le type de l'élément de la collection
<literal>type</literal> (requis) : le type de l'élément de la collection
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Une <emphasis>association plusieurs-vers-plusieurs</emphasis> est spécifiée en
utilisant l'élément <literal>&lt;many-to-many&gt;</literal>.
Une <emphasis>association plusieurs-vers-plusieurs</emphasis> est spécifiée en
utilisant l'élément <literal>&lt;many-to-many&gt;</literal>.
</para>
<programlistingco>
@ -494,63 +496,63 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<calloutlist>
<callout arearefs="manytomany1">
<para>
<literal>column</literal> (optionnel) : le nom de la colonne de la clef étrangère de l'élément
<literal>column</literal> (optionnel) : le nom de la colonne de la clef étrangère de l'élément
</para>
</callout>
<callout arearefs="manytomany2">
<para>
<literal>formula</literal> (optionnel) :
une formule SQL utilisée pour évaluer la valeur de la clef étrangère de l'élément
une formule SQL utilisée pour évaluer la valeur de la clef étrangère de l'élément
</para>
</callout>
<callout arearefs="manytomany3">
<para>
<literal>class</literal> (requis) : le nom de la classe associée
<literal>class</literal> (requis) : le nom de la classe associée
</para>
</callout>
<callout arearefs="manytomany4">
<para>
<literal>fetch</literal> (optionnel - par défaut <literal>join</literal>) :
active les récupérations par jointures externes ou par selects séquentiels pour cette association.
C'est un cas spécial ; pour une récupération complète sans attente (dans un seul <literal>SELECT</literal>) d'une
entité et de ses relations plusieurs-vers-plusieurs vers d'autres entités,
vous devriez activer la récupération <literal>join</literal> non seulement sur
la collection elle-même, mais aussi avec cet attribut sur l'élément imbriqué
<literal>fetch</literal> (optionnel - par défaut <literal>join</literal>) :
active les récupérations par jointures externes ou par selects séquentiels pour cette association.
C'est un cas spécial ; pour une récupération complète sans attente (dans un seul <literal>SELECT</literal>) d'une
entité et de ses relations plusieurs-vers-plusieurs vers d'autres entités,
vous devriez activer la récupération <literal>join</literal> non seulement sur
la collection elle-même, mais aussi avec cet attribut sur l'élément imbriqué
<literal>&lt;many-to-many&gt;</literal>.
</para>
</callout>
<callout arearefs="manytomany5">
<para>
<literal>unique</literal> (optionnel) : activer la génération DDL d'une
contrainte d'unicité pour la colonne de la clef étrangère. Ça rend la pluralité
<literal>unique</literal> (optionnel) : activer la génération DDL d'une
contrainte d'unicité pour la colonne de la clef étrangère. Ça rend la pluralité
de l'association effectivement un-vers-plusieurs.
</para>
</callout>
<callout arearefs="manytomany6">
<para>
<literal>not-found</literal> (optionnel - par défaut <literal>exception</literal>) :
spécifie comment les clefs étrangères qui référencent la lignes
manquantes seront gérées : <literal>ignore</literal> traitera
<literal>not-found</literal> (optionnel - par défaut <literal>exception</literal>) :
spécifie comment les clefs étrangères qui référencent la lignes
manquantes seront gérées : <literal>ignore</literal> traitera
une ligne manquante comme une association nulle.
</para>
</callout>
<callout arearefs="manytomany7">
<para>
<literal>entity-name</literal> (optionnel) : le nom de l'entité de la classe associée, comme une alternative à <literal>class</literal>
<literal>entity-name</literal> (optionnel) : le nom de l'entité de la classe associée, comme une alternative à <literal>class</literal>
</para>
</callout>
<callout arearefs="manytomany8">
<para>
<literal>property-ref</literal> (optionnel) : le nom d'une propriété de
la classe associée qui est jointe à cette clef étrangère. Si non spécifiée,
la clef primaire de la classe associée est utilisée.
<literal>property-ref</literal> (optionnel) : le nom d'une propriété de
la classe associée qui est jointe à cette clef étrangère. Si non spécifiée,
la clef primaire de la classe associée est utilisée.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Quelques exemples, d'abord, un ensemble de chaînes de caractères :
Quelques exemples, d'abord, un ensemble de chaînes de caractères :
</para>
<programlisting><![CDATA[<set name="names" table="person_names">
@ -559,7 +561,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</set>]]></programlisting>
<para>
Un bag contenant des entiers (avec un ordre d'itération déterminé par l'attribut <literal>order-by</literal>) :
Un bag contenant des entiers (avec un ordre d'itération déterminé par l'attribut <literal>order-by</literal>) :
</para>
<programlisting><![CDATA[<bag name="sizes"
@ -570,7 +572,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</bag>]]></programlisting>
<para>
Un tableau d'entités - dans ce cas, une association plusieurs-vers-plusieurs :
Un tableau d'entités - dans ce cas, une association plusieurs-vers-plusieurs :
</para>
<programlisting><![CDATA[<array name="addresses"
@ -582,7 +584,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</array>]]></programlisting>
<para>
Une map de chaînes de caractères vers des dates :
Une map de chaînes de caractères vers des dates :
</para>
<programlisting><![CDATA[<map name="holidays"
@ -616,26 +618,26 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<para>
Une <emphasis>association un vers plusieurs</emphasis> lie les tables de deux classes
par une clef étrangère, sans l'intervention d'une table de collection. Ce mapping perd certaines sémantiques des collections Java normales :
par une clef étrangère, sans l'intervention d'une table de collection. Ce mapping perd certaines sémantiques des collections Java normales :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Une instance de la classe de l'entité contenue ne peut pas appartenir à plus d'une
Une instance de la classe de l'entité contenue ne peut pas appartenir à plus d'une
instance de la collection
</para>
</listitem>
<listitem>
<para>
Une instance de la classe de l'entité contenue ne peut pas apparaître plus plus d'une valeur d'index de la collection
Une instance de la classe de l'entité contenue ne peut pas apparaître plus plus d'une valeur d'index de la collection
</para>
</listitem>
</itemizedlist>
<para>
Une association de <literal>Product</literal> vers <literal>Part</literal> requiert l'existence d'une
clef étrangère et possiblement une colonne d'index pour la table <literal>Part</literal>. Une balise
clef étrangère et possiblement une colonne d'index pour la table <literal>Part</literal>. Une balise
<literal>&lt;one-to-many&gt;</literal> indique que c'est une association un vers plusieurs.
</para>
@ -655,41 +657,41 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<calloutlist>
<callout arearefs="onetomany1">
<para>
<literal>class</literal> (requis) : le nom de la classe associée
<literal>class</literal> (requis) : le nom de la classe associée
</para>
</callout>
<callout arearefs="onetomany2">
<para>
<literal>not-found</literal> (optionnel - par défaut <literal>exception</literal>) :
spécifie comment les identifiants cachés qui référencent des lignes manquantes seront gérés :
<literal>not-found</literal> (optionnel - par défaut <literal>exception</literal>) :
spécifie comment les identifiants cachés qui référencent des lignes manquantes seront gérés :
<literal>ignore</literal> traitera une ligne manquante comme une association nulle
</para>
</callout>
<callout arearefs="onetomany3">
<para>
<literal>entity-name</literal> (optionnel) : le nom de l'entité de la
classe associée, comme une alternative à <literal>class</literal>.
<literal>entity-name</literal> (optionnel) : le nom de l'entité de la
classe associée, comme une alternative à <literal>class</literal>.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Notez que l'élément <literal>&lt;one-to-many&gt;</literal> n'a pas besoin de déclarer de colonnes. Il n'est pas non plus nécessaire de spécifier le nom de la table nulle part.
Notez que l'élément <literal>&lt;one-to-many&gt;</literal> n'a pas besoin de déclarer de colonnes. Il n'est pas non plus nécessaire de spécifier le nom de la table nulle part.
</para>
<para>
<emphasis>Note très importante :</emphasis> si la colonne de la clef d'une association
<literal>&lt;one-to-many&gt;</literal> est déclarée <literal>NOT NULL</literal>, vous devez déclarer le
<emphasis>Note très importante :</emphasis> si la colonne de la clef d'une association
<literal>&lt;one-to-many&gt;</literal> est déclarée <literal>NOT NULL</literal>, vous devez déclarer le
mapping de <literal>&lt;key&gt;</literal> avec <literal>not-null="true"</literal> ou
<emphasis>utiliser une association bidirectionnelle</emphasis> avec le mapping de la
collection marqué <literal>inverse="true"</literal>. Voir la discussion sur les associations bidirectionnelles plus tard dans ce chapitre.
collection marqué <literal>inverse="true"</literal>. Voir la discussion sur les associations bidirectionnelles plus tard dans ce chapitre.
</para>
<para>
Cet exemple montre une map d'entités <literal>Part</literal> par nom (où
<literal>partName</literal> est une propriété persistante de <literal>Part</literal>).
Notez l'utilisation d'un index basé sur une formule.
Cet exemple montre une map d'entités <literal>Part</literal> par nom (où
<literal>partName</literal> est une propriété persistante de <literal>Part</literal>).
Notez l'utilisation d'un index basé sur une formule.
</para>
<programlisting><![CDATA[<map name="parts"
@ -703,14 +705,14 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</sect1>
<sect1 id="collections-advancedmappings">
<title>Mappings de collection avancés</title>
<title>Mappings de collection avancés</title>
<sect2 id="collections-sorted" revision="2">
<title>Collections triées</title>
<title>Collections triées</title>
<para>
Hibernate supporte des collections implémentant <literal>java.util.SortedMap</literal> et
<literal>java.util.SortedSet</literal>. Vous devez spécifier un comparateur dans le fichier de mapping :
Hibernate supporte des collections implémentant <literal>java.util.SortedMap</literal> et
<literal>java.util.SortedSet</literal>. Vous devez spécifier un comparateur dans le fichier de mapping :
</para>
<programlisting><![CDATA[<set name="aliases"
@ -728,21 +730,21 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<para>
Les valeurs permises pour l'attribut <literal>sort</literal> sont <literal>unsorted</literal>,
<literal>natural</literal> et le nom d'une classe implémentant
<literal>natural</literal> et le nom d'une classe implémentant
<literal>java.util.Comparator</literal>.
</para>
<para>
Les collections triées se comportent réellement comme <literal>java.util.TreeSet</literal> ou
Les collections triées se comportent réellement comme <literal>java.util.TreeSet</literal> ou
<literal>java.util.TreeMap</literal>.
</para>
<para>
Si vous voulez que la base de données elle-même ordonne les éléments de la collection, utilisez l'attribut
Si vous voulez que la base de données elle-même ordonne les éléments de la collection, utilisez l'attribut
<literal>order-by</literal> des mappings <literal>set</literal>, <literal>bag</literal>
ou <literal>map</literal>. Cette solution est seulement disponible à partir du JDK 1.4 (c'est
implémenté en utilisant <literal>LinkedHashSet</literal> ou
<literal>LinkedHashMap</literal>). Ceci exécute le tri dans la requête SQL, pas en mémoire.
ou <literal>map</literal>. Cette solution est seulement disponible à partir du JDK 1.4 (c'est
implémenté en utilisant <literal>LinkedHashSet</literal> ou
<literal>LinkedHashMap</literal>). Ceci exécute le tri dans la requête SQL, pas en mémoire.
</para>
<programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
@ -761,7 +763,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</para>
<para>
Les associations peuvent même être triées sur des critères arbitraires à l'exécution en utilisant un <literal>filter()</literal> de collection.
Les associations peuvent même être triées sur des critères arbitraires à l'exécution en utilisant un <literal>filter()</literal> de collection.
</para>
<programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
@ -772,14 +774,14 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<title>Associations bidirectionnelles</title>
<para>
Une <emphasis>association bidirectionnelle</emphasis> permet une navigation à
partir de la "fin" de l'association. Deux sortes d'associations bidirectionnelles sont supportées :
Une <emphasis>association bidirectionnelle</emphasis> permet une navigation à
partir de la "fin" de l'association. Deux sortes d'associations bidirectionnelles sont supportées :
<variablelist>
<varlistentry>
<term>un-vers-plusieurs (NdT : one-to-many)</term>
<listitem>
<para>
ensemble ou sac à une extrémité, une seule valeur à l'autre
ensemble ou sac à une extrémité, une seule valeur à l'autre
</para>
</listitem>
</varlistentry>
@ -787,7 +789,7 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
<term>plusieurs-vers-plusieurs (NdT : many-to-many)</term>
<listitem>
<para>
ensemble ou sac aux deux extrémités
ensemble ou sac aux deux extrémités
</para>
</listitem>
</varlistentry>
@ -795,13 +797,13 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</para>
<para>
Vous pouvez spécifier une association plusieurs-vers-plusieurs bidirectionnelle simplement
en mappant deux associations plusieurs-vers-plusieurs vers la même table de base de données et en déclarant une extrémité comme <emphasis>inverse</emphasis> (celle de votre choix, mais ça ne peut pas être une collection indexée).
Vous pouvez spécifier une association plusieurs-vers-plusieurs bidirectionnelle simplement
en mappant deux associations plusieurs-vers-plusieurs vers la même table de base de données et en déclarant une extrémité comme <emphasis>inverse</emphasis> (celle de votre choix, mais ça ne peut pas être une collection indexée).
</para>
<para>
Voici un exemple d'association bidirectionnelle plusieurs-vers-plusieurs ; chaque catégorie peut
avoir plusieurs objets et chaque objet peut être dans plusieurs catégories :
Voici un exemple d'association bidirectionnelle plusieurs-vers-plusieurs ; chaque catégorie peut
avoir plusieurs objets et chaque objet peut être dans plusieurs catégories :
</para>
<programlisting><![CDATA[<class name="Category">
@ -825,28 +827,28 @@ kittens = cat.getKittens(); // Ok, la collection kittens est un Set
</class>]]></programlisting>
<para>
Les changements faits uniquement sur l'extréminté inverse de l'association <emphasis>ne sont pas</emphasis>
persistés. Ceci signifie qu'Hibernate a deux représentations en mémoire pour chaque
Les changements faits uniquement sur l'extréminté inverse de l'association <emphasis>ne sont pas</emphasis>
persistés. Ceci signifie qu'Hibernate a deux représentations en mémoire pour chaque
association bidirectionnelles, un lien de A vers B et un autre de B vers A. C'est
plus facile à comprendre si vous pensez au modèle objet de Java et comment nous
créons une relation plusieurs-vers-plusieurs en Java :
plus facile à comprendre si vous pensez au modèle objet de Java et comment nous
créons une relation plusieurs-vers-plusieurs en Java :
</para>
<programlisting><![CDATA[
category.getItems().add(item); // La catégorie est maintenant "au courant" de la relation
category.getItems().add(item); // La catégorie est maintenant "au courant" de la relation
item.getCategories().add(category); // L'objet est maintenant "au courant" de la relation
session.persist(item); // La relation ne sera pas sauvegardée !
session.persist(category); // La relation sera sauvegardée]]></programlisting>
session.persist(item); // La relation ne sera pas sauvegardée !
session.persist(category); // La relation sera sauvegardée]]></programlisting>
<para>
La partie non-inverse est utilisée pour sauvegarder la représentation en mémoire dans la base de données.
La partie non-inverse est utilisée pour sauvegarder la représentation en mémoire dans la base de données.
</para>
<para>
Vous pouvez définir une association un-vers-plusieurs bidirectionnelle en mappant une
association un-vers-plusieurs vers la(es) même(s) colonne(s) de table qu'une association
plusieurs-vers-un et en déclarant l'extrémité pluri-valuée <literal>inverse="true"</literal>.
Vous pouvez définir une association un-vers-plusieurs bidirectionnelle en mappant une
association un-vers-plusieurs vers la(es) même(s) colonne(s) de table qu'une association
plusieurs-vers-un et en déclarant l'extrémité pluri-valuée <literal>inverse="true"</literal>.
</para>
<programlisting><![CDATA[<class name="Parent">
@ -868,19 +870,19 @@ session.persist(category); // La relation sera sauvegard
</class>]]></programlisting>
<para>
Mapper une extrémité d'une association avec <literal>inverse="true"</literal> n'affecte
pas l'opération de cascades, ce sont des concepts orthogonaux !
Mapper une extrémité d'une association avec <literal>inverse="true"</literal> n'affecte
pas l'opération de cascades, ce sont des concepts orthogonaux !
</para>
</sect2>
<sect2 id="collections-indexedbidirectional">
<title>Associations bidirectionnelles avec des collections indexées</title>
<title>Associations bidirectionnelles avec des collections indexées</title>
<para>
Une association bidirectionnelle où une extrémité est représentée comme une <literal>&lt;list&gt;</literal>
ou une <literal>&lt;map&gt;</literal> requiert une considération spéciale. Si il y a une
propriété de la classe enfant qui mappe la colonne de l'index, pas de problème, nous pouvons
continuer à utiliser <literal>inverse="true"</literal> sur le mapping de la collection :
Une association bidirectionnelle où une extrémité est représentée comme une <literal>&lt;list&gt;</literal>
ou une <literal>&lt;map&gt;</literal> requiert une considération spéciale. Si il y a une
propriété de la classe enfant qui mappe la colonne de l'index, pas de problème, nous pouvons
continuer à utiliser <literal>inverse="true"</literal> sur le mapping de la collection :
</para>
<programlisting><![CDATA[<class name="Parent">
@ -906,10 +908,10 @@ session.persist(category); // La relation sera sauvegard
</class>]]></programlisting>
<para>
Mais, si il n'y a pas de telle prorpriété sur la classe enfant, nous ne pouvons pas penser
à l'association comme vraiment bidirectionnelle (il y a des informations disponibles à une
extrémité de l'association qui ne sont pas disponibles à l'autre extrémité). Dans ce cas,
nous ne pouvons pas mapper la collection <literal>inverse="true"</literal>. À la place, nous
Mais, si il n'y a pas de telle prorpriété sur la classe enfant, nous ne pouvons pas penser
à l'association comme vraiment bidirectionnelle (il y a des informations disponibles à une
extrémité de l'association qui ne sont pas disponibles à l'autre extrémité). Dans ce cas,
nous ne pouvons pas mapper la collection <literal>inverse="true"</literal>. À la place, nous
pourrions utiliser le mapping suivant :
</para>
@ -937,8 +939,8 @@ session.persist(category); // La relation sera sauvegard
</class>]]></programlisting>
<para>
Notez que dans ce mapping, l'extrémité de l'association contenant la collection est responsable
des mises à jour de la clef étrangère. À faire : cela entraîne-t-il réellement des expressions
Notez que dans ce mapping, l'extrémité de l'association contenant la collection est responsable
des mises à jour de la clef étrangère. À faire : cela entraîne-t-il réellement des expressions
updates inutiles ?
</para>
@ -965,12 +967,12 @@ session.persist(category); // La relation sera sauvegard
</map>]]></programlisting>
<para>
Une seconde approche est simplement de remodeler l'association comme une classe d'entité. C'est
Une seconde approche est simplement de remodeler l'association comme une classe d'entité. C'est
l'approche la plus commune.
</para>
<para>
Une alternative finale est d'utiliser des éléments composites, dont nous discuterons plus tard.
Une alternative finale est d'utiliser des éléments composites, dont nous discuterons plus tard.
</para>
</sect2>
@ -979,21 +981,21 @@ session.persist(category); // La relation sera sauvegard
<title>Utiliser un <literal>&lt;idbag&gt;</literal></title>
<para>
Si vous embrassez pleinement notre vue que les clefs composées sont une mauvaise
chose et que des entités devraient avoir des identifiants artificiels (des clefs
subrogées), alors vous pourriez trouver un peu curieux que les associations
plusieurs-vers-plusieurs et les collections de valeurs que nous avons montré jusqu'ici
mappent toutes des tables avec des clefs composées ! Maintenant, ce point est assez
discutable ; une table d'association pure ne semble pas beaucoup bénéficier d'une clef
subrogée (bien qu'une collection de valeur composées le <emphasis>pourrait</emphasis>).
Néanmoins, Hibernate fournit une foncionnalité qui vous permet de mapper
Si vous embrassez pleinement notre vue que les clefs composées sont une mauvaise
chose et que des entités devraient avoir des identifiants artificiels (des clefs
subrogées), alors vous pourriez trouver un peu curieux que les associations
plusieurs-vers-plusieurs et les collections de valeurs que nous avons montré jusqu'ici
mappent toutes des tables avec des clefs composées ! Maintenant, ce point est assez
discutable ; une table d'association pure ne semble pas beaucoup bénéficier d'une clef
subrogée (bien qu'une collection de valeur composées le <emphasis>pourrait</emphasis>).
Néanmoins, Hibernate fournit une foncionnalité qui vous permet de mapper
des associations plusieurs-vers-plusieurs et des collections de valeurs vers une
table avec une clef subrogée.
table avec une clef subrogée.
</para>
<para>
L'élément <literal>&lt;idbag&gt;</literal> vous laisse mapper une <literal>List</literal>
(ou une <literal>Collection</literal>) avec une sémantique de sac.
L'élément <literal>&lt;idbag&gt;</literal> vous laisse mapper une <literal>List</literal>
(ou une <literal>Collection</literal>) avec une sémantique de sac.
</para>
<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
@ -1005,22 +1007,22 @@ session.persist(category); // La relation sera sauvegard
</idbag>]]></programlisting>
<para>
Comme vous pouvez voir, un <literal>&lt;idbag&gt;</literal> a un généréteur d'id
artificiel, comme une classe d'entité ! Une clef subrogée différente est assignée
à chaque ligne de la collection. Cependant, Hibernate ne fournit pas de mécanisme pour
découvrir la valeur d'une clef subrogée d'une ligne particulière.
Comme vous pouvez voir, un <literal>&lt;idbag&gt;</literal> a un généréteur d'id
artificiel, comme une classe d'entité ! Une clef subrogée différente est assignée
à chaque ligne de la collection. Cependant, Hibernate ne fournit pas de mécanisme pour
découvrir la valeur d'une clef subrogée d'une ligne particulière.
</para>
<para>
Notez que les performances de la mise à jour d'un <literal>&lt;idbag&gt;</literal>
Notez que les performances de la mise à jour d'un <literal>&lt;idbag&gt;</literal>
sont <emphasis>bien</emphasis> meilleures qu'un <literal>&lt;bag&gt;</literal> ordinaire !
Hibernate peut localiser des lignes individuelles efficacement et les mettre à jour ou
Hibernate peut localiser des lignes individuelles efficacement et les mettre à jour ou
les effacer individuellement, comme une liste, une map ou un ensemble.
</para>
<para>
Dans l'implémentation actuelle, la stratégie de la génération de l'identifiant <literal>native</literal>
n'est pas supportée pour les identifiants de collection <literal>&lt;idbag&gt;</literal>.
Dans l'implémentation actuelle, la stratégie de la génération de l'identifiant <literal>native</literal>
n'est pas supportée pour les identifiants de collection <literal>&lt;idbag&gt;</literal>.
</para>
</sect2>
@ -1045,7 +1047,7 @@ session.persist(category); // La relation sera sauvegard
<title>Exemples de collections</title>
<para>
Les sections précédentes sont assez confuses. Donc prenons un exemple. Cette classe :
Les sections précédentes sont assez confuses. Donc prenons un exemple. Cette classe :
</para>
<programlisting><![CDATA[package eg;
@ -1093,7 +1095,7 @@ public class Parent {
</hibernate-mapping>]]></programlisting>
<para>
Ceci mappe les définitions de tables suivantes :
Ceci mappe les définitions de tables suivantes :
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
@ -1139,7 +1141,7 @@ alter table child add constraint childfk0 (parent_id) references parent]]></prog
<para>
Alternativement, si vous insistez absolument pour que cette association soit unidirectionnelle,
vous pouvez déclarer la contrainte <literal>NOT NULL</literal> sur le mapping <literal>&lt;key&gt;</literal> :
vous pouvez déclarer la contrainte <literal>NOT NULL</literal> sur le mapping <literal>&lt;key&gt;</literal> :
</para>
<programlisting><![CDATA[<hibernate-mapping>
@ -1164,8 +1166,8 @@ alter table child add constraint childfk0 (parent_id) references parent]]></prog
</hibernate-mapping>]]></programlisting>
<para>
D'un autre côté, si un enfant pouvait avoir plusieurs parent, une association
plusieurs-vers-plusieurs est plus appropriée :
D'un autre côté, si un enfant pouvait avoir plusieurs parent, une association
plusieurs-vers-plusieurs est plus appropriée :
</para>
<programlisting><![CDATA[<hibernate-mapping>
@ -1190,7 +1192,7 @@ alter table child add constraint childfk0 (parent_id) references parent]]></prog
</hibernate-mapping>]]></programlisting>
<para>
Définitions des tables :
Définitions des tables :
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
@ -1202,12 +1204,12 @@ alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
<para>
Pour plus d'exemples et une revue complète du mapping de la relation parent/enfant, voir
Pour plus d'exemples et une revue complète du mapping de la relation parent/enfant, voir
see <xref linkend="example-parentchild"/>.
</para>
<para>
Des mappings d'association plus exotiques sont possibles, nous cataloguerons toutes les possibilités
Des mappings d'association plus exotiques sont possibles, nous cataloguerons toutes les possibilités
dans le prochain chapitre.
</para>

View File

@ -1,21 +1,23 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="components">
<title>Mapping de composants</title>
<para>
La notion de <emphasis>composants</emphasis> est réutilisé dans différents contextes,
avec différents objectifs, à travers Hibernate.
La notion de <emphasis>composants</emphasis> est réutilisé dans différents contextes,
avec différents objectifs, à travers Hibernate.
</para>
<sect1 id="components-dependentobjects" revision="2" >
<title>Objects dépendants</title>
<title>Objects dépendants</title>
<para>
Le composant est un objet inclu dans un autre qui est sauvegardé comme une valeur, et
non pas comme une entité.
Le composant fait référence à la notion (au sens objet) de composition
Le composant est un objet inclu dans un autre qui est sauvegardé comme une valeur, et
non pas comme une entité.
Le composant fait référence à la notion (au sens objet) de composition
(et non pas de composant au sens d'architecture de composants).
Par exemple on pourrait modélisé l'objet personne de cette façon:
Par exemple on pourrait modélisé l'objet personne de cette façon:
</para>
<programlisting><![CDATA[public class Person {
@ -69,14 +71,14 @@
}]]></programlisting>
<para>
Maintenant <literal>Name</literal> peut-être sauvegardé comme un composant de
<literal>Person</literal>. Remarquer que <literal>Name</literal> définit des methodes
d'accès et de modification pour ses propriétés persistantes, mais il n'a pas besoin
des interfaces ou des propriétés d'identification ( par exemple getId() ) qui sont propres aux entités.
Maintenant <literal>Name</literal> peut-être sauvegardé comme un composant de
<literal>Person</literal>. Remarquer que <literal>Name</literal> définit des methodes
d'accès et de modification pour ses propriétés persistantes, mais il n'a pas besoin
des interfaces ou des propriétés d'identification ( par exemple getId() ) qui sont propres aux entités.
</para>
<para>
Nous serions alors amené à mapper ce composant de cette façon:
Nous serions alors amené à mapper ce composant de cette façon:
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
@ -100,24 +102,24 @@
</para>
<para>
Comme tous les types valeurs, les composants ne supportent pas les références partagés.
En d'autres mots, deux instances de person peuvent avoir un même nom, mais ces noms sont
indépendants, ils peuvent être identiques si on les compare par valeur mais ils représentent
deux objets distincts en mémoire. La notion de nullité pour un composant est
Comme tous les types valeurs, les composants ne supportent pas les références partagés.
En d'autres mots, deux instances de person peuvent avoir un même nom, mais ces noms sont
indépendants, ils peuvent être identiques si on les compare par valeur mais ils représentent
deux objets distincts en mémoire. La notion de nullité pour un composant est
<emphasis>ad hoc</emphasis>. Quand il recharge l'objet qui contient le composant, Hibernate
supposera que si tous les champs du composants sont nuls alors le composant sera positionné
à la valeur null. Ce choix programmatif devrait être satisfaisant dans la plupart des cas.
supposera que si tous les champs du composants sont nuls alors le composant sera positionné
à la valeur null. Ce choix programmatif devrait être satisfaisant dans la plupart des cas.
</para>
<para>
Les propriétés d'un composant peuvent être de tous les types qu'Hibernate supporte habituellement
Les propriétés d'un composant peuvent être de tous les types qu'Hibernate supporte habituellement
(collections, many-to-one associations, autres composants, etc). Les composants inclus ne doivent <emphasis>pas</emphasis>
être vus comme quelque chose d'exotique. Hibernate a été conçu pour supporter un modèle objet très granulaire.
être vus comme quelque chose d'exotique. Hibernate a été conçu pour supporter un modèle objet très granulaire.
</para>
<para>
Le <literal>&lt;component&gt;</literal> peut inclure dans la liste de ses propriétés
une référence au <literal>&lt;parent&gt;</literal> conteneur.
Le <literal>&lt;component&gt;</literal> peut inclure dans la liste de ses propriétés
une référence au <literal>&lt;parent&gt;</literal> conteneur.
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
@ -126,7 +128,7 @@
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- référence arrière à Person -->
<parent name="namedPerson"/> <!-- référence arrière à Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
@ -136,11 +138,11 @@
</sect1>
<sect1 id="components-incollections" revision="1">
<title>Collection d'objets dépendants</title>
<title>Collection d'objets dépendants</title>
<para>
Les collections d'objets dépendants sont supportés (exemple: un tableau de type
<literal>Name</literal>). Déclarer la collection de composants en remplaçant le tag <literal>&lt;element&gt;</literal>
Les collections d'objets dépendants sont supportés (exemple: un tableau de type
<literal>Name</literal>). Déclarer la collection de composants en remplaçant le tag <literal>&lt;element&gt;</literal>
par le tag <literal>&lt;composite-element&gt;</literal>.
</para>
@ -154,39 +156,39 @@
</set>]]></programlisting>
<para>
Remarque: Si vous définissez un <literal>Set</literal> d'élément composite,
il est très important d'implémenter la méthode <literal>equals()</literal> et
Remarque: Si vous définissez un <literal>Set</literal> d'élément composite,
il est très important d'implémenter la méthode <literal>equals()</literal> et
<literal>hashCode()</literal> correctement.
</para>
<para>
Les élements composite peuvent aussi contenir des composants mais pas des collections.
Si votre élément composite contient aussi des composants, utilisez l'élément <literal>&lt;nested-composite-element&gt;</literal>
. Une collections de composants qui ccontiennent eux-mêmes des composants est un cas très exotique.
A ce stade demandez-vous si une association un-à-plusieurs ne serait pas plus approprié.
Essayez de re remodeler votre élément composite comme une entité ( Dans ce cas même si le modèle
Java est le même la logique de persitence et de relation sont tout de même différentes)
Les élements composite peuvent aussi contenir des composants mais pas des collections.
Si votre élément composite contient aussi des composants, utilisez l'élément <literal>&lt;nested-composite-element&gt;</literal>
. Une collections de composants qui ccontiennent eux-mêmes des composants est un cas très exotique.
A ce stade demandez-vous si une association un-à-plusieurs ne serait pas plus approprié.
Essayez de re remodeler votre élément composite comme une entité ( Dans ce cas même si le modèle
Java est le même la logique de persitence et de relation sont tout de même différentes)
</para>
<para>
Remarque, le mapping d'éléments composites ne supporte pas la nullité des
propriétés lorsqu'on utilise un <literal>&lt;set&gt;</literal>. Hibernate
Remarque, le mapping d'éléments composites ne supporte pas la nullité des
propriétés lorsqu'on utilise un <literal>&lt;set&gt;</literal>. Hibernate
lorsqu'il supprime un objet utilise chaque colonne pour identifier un objet
(on ne peut pas utiliser des clés primaires distinctes dans une table d'éléments composites),
ce qui n'est pas possible avec des valeurs nulles. Vous devez donc choisir d'interdire la nullité
des propriétés d'un élément composite ou choisir un autre type de collection comme :
(on ne peut pas utiliser des clés primaires distinctes dans une table d'éléments composites),
ce qui n'est pas possible avec des valeurs nulles. Vous devez donc choisir d'interdire la nullité
des propriétés d'un élément composite ou choisir un autre type de collection comme :
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal> ou <literal>&lt;idbag&gt;</literal>.
</para>
<para>
Un cas particulier d'élément composite est un élément composite qui inclut un élément
Un cas particulier d'élément composite est un élément composite qui inclut un élément
<literal>&lt;many-to-one&gt;</literal>. Un mapping comme celui-ci
vous permet d'associer les colonnes d'une table d'association plusieurs à plusieurs (many-to-many)
à la classse de l'élément composite. L'exemple suivant est une association plusieurs à plusieurs
de <literal>Order</literal> à <literal>Item</literal> à
vous permet d'associer les colonnes d'une table d'association plusieurs à plusieurs (many-to-many)
à la classse de l'élément composite. L'exemple suivant est une association plusieurs à plusieurs
de <literal>Order</literal> à <literal>Item</literal> à
<literal>purchaseDate</literal>, <literal>price</literal> et
<literal>quantity</literal> sont des propriétés de l'association.
<literal>quantity</literal> sont des propriétés de l'association.
</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
@ -203,12 +205,12 @@
</class>]]></programlisting>
<para>
Bien sûr, il ne peut pas y avoir de référence à l'achat (purchase) depuis l'article (item), pour
pouvoir naviguer de façon bidirectionnelle dans l'association. N'oubliez pas que les composants
sont de type valeurs et n'autorise pas les références partagées.
Bien sûr, il ne peut pas y avoir de référence à l'achat (purchase) depuis l'article (item), pour
pouvoir naviguer de façon bidirectionnelle dans l'association. N'oubliez pas que les composants
sont de type valeurs et n'autorise pas les références partagées.
</para>
<para>Même les associations ternaires ou quaternaires sont possibles:</para>
<para>Même les associations ternaires ou quaternaires sont possibles:</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
@ -222,8 +224,8 @@
</class>]]></programlisting>
<para>
Les éléments composites peuvent apparaître dans les requêtes en utilisant
la même syntaxe que associations
Les éléments composites peuvent apparaître dans les requêtes en utilisant
la même syntaxe que associations
</para>
</sect1>
@ -232,9 +234,9 @@
<title>Utiliser les composants comme index de map</title>
<para>
l'élément <literal>&lt;composite-map-key&gt;</literal>
l'élément <literal>&lt;composite-map-key&gt;</literal>
vous permet d'utiliser une classe de composant comme indice de
<literal>Map</literal>. Assurez-vous d'avoir surdéfini
<literal>Map</literal>. Assurez-vous d'avoir surdéfini
<literal>hashCode()</literal> et <literal>equals()</literal> dans la
classe du composant.
</para>
@ -244,42 +246,42 @@
<title>Utiliser un composant comme identifiant</title>
<para>
Vous pouvez utiliser un composant comme identifiant d'une entité.
Mais pour cela la classe du composant doit respecter certaines règles.
Vous pouvez utiliser un composant comme identifiant d'une entité.
Mais pour cela la classe du composant doit respecter certaines règles.
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Elle doit implémenter <literal>java.io.Serializable</literal>.
Elle doit implémenter <literal>java.io.Serializable</literal>.
</para>
</listitem>
<listitem>
<para>
Elle doit redéfinir <literal>equals()</literal> et
<literal>hashCode()</literal>, de façon cohérente avec le
fait qu'elle définit une clé composite dans la base de
données.
Elle doit redéfinir <literal>equals()</literal> et
<literal>hashCode()</literal>, de façon cohérente avec le
fait qu'elle définit une clé composite dans la base de
données.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>
Remarque: avec hibernate3, la seconde règle n'est plus absolument
necessaire mais faîtes le quand même.</emphasis>
Remarque: avec hibernate3, la seconde règle n'est plus absolument
necessaire mais faîtes le quand même.</emphasis>
</para>
<para>
Vous ne pouvez pas utiliser de <literal>IdentifierGenerator</literal> pour générer
une clé composite, l'application devra définir elle même ses propres identifiants.
Vous ne pouvez pas utiliser de <literal>IdentifierGenerator</literal> pour générer
une clé composite, l'application devra définir elle même ses propres identifiants.
</para>
<para>
Utiliser l'élément <literal>&lt;composite-id&gt;</literal> (en incluant l'élément
<literal>&lt;key-property&gt;</literal>) à la place de l'habituel déclaration
Utiliser l'élément <literal>&lt;composite-id&gt;</literal> (en incluant l'élément
<literal>&lt;key-property&gt;</literal>) à la place de l'habituel déclaration
<literal>&lt;id&gt;</literal>. Par exemple la classe
<literal>OrderLine</literal> qui dépend de la clé primaire
<literal>OrderLine</literal> qui dépend de la clé primaire
(composite) de <literal>Order</literal>.
</para>
@ -303,9 +305,9 @@
</class>]]></programlisting>
<para>
Maintenant toutes clés étrangères référençant la table <literal>OrderLine</literal>
devra aussi être composite. Vous devez en tenir compte lorsque vous écrivez vos mapping d'association pour les autres classes.
Une association à <literal>OrderLine</literal> devrait être mappé de la façon suivante :
Maintenant toutes clés étrangères référençant la table <literal>OrderLine</literal>
devra aussi être composite. Vous devez en tenir compte lorsque vous écrivez vos mapping d'association pour les autres classes.
Une association à <literal>OrderLine</literal> devrait être mappé de la façon suivante :
</para>
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
@ -316,13 +318,13 @@
</many-to-one>]]></programlisting>
<para>
(Remarque: l'élément <literal>&lt;column&gt;</literal> est une alternative à l'attribut
(Remarque: l'élément <literal>&lt;column&gt;</literal> est une alternative à l'attribut
<literal>column</literal> que l'on utilise partout.)
</para>
<para>
Une association <literal>plusieurs-à-plusieurs</literal> (many-to-many) à <literal>OrderLine</literal>
utilisera aussi une clé étrangère composite:
Une association <literal>plusieurs-à-plusieurs</literal> (many-to-many) à <literal>OrderLine</literal>
utilisera aussi une clé étrangère composite:
</para>
<programlisting><![CDATA[<set name="undeliveredOrderLines">
@ -348,12 +350,12 @@
</set>]]></programlisting>
<para>
(L'élément <literal>&lt;one-to-many&gt;</literal>, comme d'habitude, ne déclare pas de colonne.)
(L'élément <literal>&lt;one-to-many&gt;</literal>, comme d'habitude, ne déclare pas de colonne.)
</para>
<para>
Si <literal>OrderLine</literal> lui-même possède une collection, celle-ci aura aussi
une clé composite étrangère.
Si <literal>OrderLine</literal> lui-même possède une collection, celle-ci aura aussi
une clé composite étrangère.
</para>
<programlisting><![CDATA[<class name="OrderLine">
@ -378,7 +380,7 @@
<title>Composant Dynamique</title>
<para>
Vous pouvez même mapper une propriété de type <literal>Map</literal>:
Vous pouvez même mapper une propriété de type <literal>Map</literal>:
</para>
<programlisting><![CDATA[<dynamic-component name="userAttributes">
@ -388,12 +390,12 @@
</dynamic-component>]]></programlisting>
<para>
La sémantique de l'association à un <literal>&lt;dynamic-component&gt;</literal>
est identique à celle que l'on utilise pour les composants.
L'avantage de ce type de mapping est qu'il pemet de déterminer les véritables propriétés
du bean au moment su déploiement en éditant simplement le document de mapping.
La sémantique de l'association à un <literal>&lt;dynamic-component&gt;</literal>
est identique à celle que l'on utilise pour les composants.
L'avantage de ce type de mapping est qu'il pemet de déterminer les véritables propriétés
du bean au moment su déploiement en éditant simplement le document de mapping.
La manipulation du document de mapping pendant l'execution de l'application est aussi
possible en utilisant un parser DOM. Il ya même mieux, vous pouvez accéder (et changer)
possible en utilisant un parser DOM. Il ya même mieux, vous pouvez accéder (et changer)
le metamodel de configuration d'hibernate en utilisant l'objet <literal>Configuration</literal>
</para>

View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="events">
<title>Les intercepteurs et les événements</title>
<title>Les intercepteurs et les événements</title>
<para>
Il est souvent utile pour l'application de réagir à certains événements
qui surviennent dans Hibernate. Cela autorise l'implémentation de certaines sortes de
fonctionnalités génériques, et d'extensions de fonctionnalités d'Hibernate.
Il est souvent utile pour l'application de réagir à certains événements
qui surviennent dans Hibernate. Cela autorise l'implémentation de certaines sortes de
fonctionnalités génériques, et d'extensions de fonctionnalités d'Hibernate.
</para>
<sect1 id="objectstate-interceptors" revision="2">
@ -13,18 +15,18 @@
<para>
L'interface <literal>Interceptor</literal> fournit des "callbacks" de la session vers l'application
et permettent à l'application de consulter et/ou de manipuler des propriétés
d'un objet persistant avant qu'il soit sauvegardé, mis à jour, supprimé ou chargé.
Une utilisation possible de cette fonctionnalité est de tracer l'accès à l'information.
et permettent à l'application de consulter et/ou de manipuler des propriétés
d'un objet persistant avant qu'il soit sauvegardé, mis à jour, supprimé ou chargé.
Une utilisation possible de cette fonctionnalité est de tracer l'accès à l'information.
Par exemple, l'<literal>Interceptor</literal> suivant positionne
<literal>createTimestamp</literal> quand un <literal>Auditable</literal> est créé
et met à jour la propriété <literal>lastUpdateTimestamp</literal> quand un
<literal>Auditable</literal> est mis à jour.
<literal>createTimestamp</literal> quand un <literal>Auditable</literal> est créé
et met à jour la propriété <literal>lastUpdateTimestamp</literal> quand un
<literal>Auditable</literal> est mis à jour.
</para>
<para>
Vous pouvez soit implémenter <literal>Interceptor</literal> directement ou (mieux)
étendre <literal>EmptyInterceptor</literal>.
Vous pouvez soit implémenter <literal>Interceptor</literal> directement ou (mieux)
étendre <literal>EmptyInterceptor</literal>.
</para>
<programlisting><![CDATA[package org.hibernate.test;
@ -115,14 +117,14 @@ public class AuditInterceptor extends EmptyInterceptor {
}]]></programlisting>
<para>
L'intercepteur doit être spécifié quand une session est créée.
L'intercepteur doit être spécifié quand une session est créée.
</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".
Dans ce cas, l'intercepteur doit être "threadsafe".
</para>
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
@ -130,49 +132,49 @@ public class AuditInterceptor extends EmptyInterceptor {
</sect1>
<sect1 id="objectstate-events" revision="3">
<title>Système d'événements</title>
<title>Système d'événements</title>
<para>
Si vous devez réagir à des événements particuliers dans votre couche de persistance,
vous pouvez aussi utiliser l'architecture d'<emphasis>événements</emphasis> d'Hibernate3.
Le système d'événements peut être utilisé en supplément ou en remplacement des interceptors.
Si vous devez réagir à des événements particuliers dans votre couche de persistance,
vous pouvez aussi utiliser l'architecture d'<emphasis>événements</emphasis> d'Hibernate3.
Le système d'événements peut être utilisé en supplément ou en remplacement des interceptors.
</para>
<para>
Essentiellement toutes les méthodes de l'interface <literal>Session</literal> sont corrélées à
un événement. Vous avez un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
Essentiellement toutes les méthodes de l'interface <literal>Session</literal> sont corrélées à
un événement. Vous avez un <literal>LoadEvent</literal>, un <literal>FlushEvent</literal>, etc
(consultez la DTD du fichier de configuration XML ou le paquet <literal>org.hibernate.event</literal>
pour avoir la liste complète des types d'événement définis).
Quand une requête est faite à partir d'une de ces méthodes, la
<literal>Session</literal> Hibernate génère un événement approprié et le passe
au listener configuré pour ce type.
Par défaut, ces listeners implémentent le même traitement dans lequel ces méthodes
pour avoir la liste complète des types d'événement définis).
Quand une requête est faite à partir d'une de ces méthodes, la
<literal>Session</literal> Hibernate génère un événement approprié et le passe
au listener configuré pour ce type.
Par défaut, ces listeners implémentent le même traitement dans lequel ces méthodes
aboutissent toujours.
Cependant, vous êtes libre d'implémenter une version personnalisée d'une de ces
interfaces de listener (c'est-à-dire, le <literal>LoadEvent</literal> est traité par
l'implémentation de l'interface <literal>LoadEventListener</literal> déclarée), dans
quel cas leur implémentation devrait être responsable du traitement des
requêtes <literal>load()</literal> faites par la <literal>Session</literal>.
Cependant, vous êtes libre d'implémenter une version personnalisée d'une de ces
interfaces de listener (c'est-à-dire, le <literal>LoadEvent</literal> est traité par
l'implémentation de l'interface <literal>LoadEventListener</literal> déclarée), dans
quel cas leur implémentation devrait être responsable du traitement des
requêtes <literal>load()</literal> faites par la <literal>Session</literal>.
</para>
<para>
Les listeners devraient effectivement être considérés comme des singletons ; dans le sens
où ils sont partagés entre des requêtes, et donc ne devraient pas sauvegarder des états
Les listeners devraient effectivement être considérés comme des singletons ; dans le sens
où ils sont partagés entre des requêtes, et donc ne devraient pas sauvegarder des états
de variables d'instance.
</para>
<para>
Un listener personnalisé devrait implémenter l'interface appropriée pour l'événement
qu'il veut traiter et/ou étendre une des classes de base (ou même l'événement prêt à
l'emploi utilisé par Hibernate comme ceux déclarés non-finaux à cette intention). Les
listeners personnalisés peuvent être soit inscrits par programmation à travers l'objet
<literal>Configuration</literal>, ou spécifiés la configuration XML d'Hibernate
(la configuration déclarative à travers le fichier de propriétés n'est pas supportée).
Voici un exemple de listener personnalisé pour l'événement de chargement :
Un listener personnalisé devrait implémenter l'interface appropriée pour l'événement
qu'il veut traiter et/ou étendre une des classes de base (ou même l'événement prêt à
l'emploi utilisé par Hibernate comme ceux déclarés non-finaux à cette intention). Les
listeners personnalisés peuvent être soit inscrits par programmation à travers l'objet
<literal>Configuration</literal>, ou spécifiés la configuration XML d'Hibernate
(la configuration déclarative à travers le fichier de propriétés n'est pas supportée).
Voici un exemple de listener personnalisé pour l'événement de chargement :
</para>
<programlisting><![CDATA[public class MyLoadListener implements LoadEventListener {
// C'est une simple méthode définie par l'interface LoadEventListener
// C'est une simple méthode définie par l'interface LoadEventListener
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
@ -182,8 +184,8 @@ public class AuditInterceptor extends EmptyInterceptor {
}]]></programlisting>
<para>
Vous avez aussi besoin d'une entrée de configuration disant à Hibernate d'utiliser
ce listener en plus du listener par défaut :
Vous avez aussi besoin d'une entrée de configuration disant à Hibernate d'utiliser
ce listener en plus du listener par défaut :
</para>
<programlisting><![CDATA[<hibernate-configuration>
@ -205,33 +207,33 @@ LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener
cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
<para>
Les listeners inscrits déclarativement ne peuvent pas partager d'instances. Si le même
nom de classe est utilisée dans plusieurs éléments <literal>&lt;listener/&gt;</literal>,
chaque référence sera une instance distincte de cette classe. Si vous avez besoin de la
faculté de partager des instances de listener entre plusieurs types de listener, vous devez
Les listeners inscrits déclarativement ne peuvent pas partager d'instances. Si le même
nom de classe est utilisée dans plusieurs éléments <literal>&lt;listener/&gt;</literal>,
chaque référence sera une instance distincte de cette classe. Si vous avez besoin de la
faculté de partager des instances de listener entre plusieurs types de listener, vous devez
utiliser l'approche d'inscription par programmation.
</para>
<para>
Pourquoi implémenter une interface et définir le type spécifique durant la configuration ?
Une implémentation de listener pourrait implémenter plusieurs interfaces de listener
d'événements. Avoir en plus le type défini durant l'inscription rend plus facile
l'activation ou la désactivation pendant la configuration.
Pourquoi implémenter une interface et définir le type spécifique durant la configuration ?
Une implémentation de listener pourrait implémenter plusieurs interfaces de listener
d'événements. Avoir en plus le type défini durant l'inscription rend plus facile
l'activation ou la désactivation pendant la configuration.
</para>
</sect1>
<sect1 id="objectstate-decl-security" revision="2">
<title>Sécurité déclarative d'Hibernate</title>
<title>Sécurité déclarative d'Hibernate</title>
<para>
Généralement, la sécurité déclarative dans les applications Hibernate est gérée dans la
couche de session. Maintenant, Hibernate3 permet à certaines actions d'être approuvées
via JACC, et autorisées via JAAS. Cette fonctionnalité optionnelle est construite
au dessus de l'architecture d'événements.
Généralement, la sécurité déclarative dans les applications Hibernate est gérée dans la
couche de session. Maintenant, Hibernate3 permet à certaines actions d'être approuvées
via JACC, et autorisées via JAAS. Cette fonctionnalité optionnelle est construite
au dessus de l'architecture d'événements.
</para>
<para>
D'abord, vous devez configurer les listeners d'événements appropriés pour permettre
D'abord, vous devez configurer les listeners d'événements appropriés pour permettre
l'utilisation d'autorisations JAAS.
</para>
@ -243,18 +245,18 @@ cfg.EventListeners().setLoadEventListeners(stack);]]></programlisting>
<para>
Notez que <literal>&lt;listener type="..." class="..."/&gt;</literal> est juste un raccourci
pour <literal>&lt;event type="..."&gt;&lt;listener class="..."/&gt;&lt;/event&gt;</literal>
quand il y a exactement un listener pour un type d'événement particulier.
quand il y a exactement un listener pour un type d'événement particulier.
</para>
<para>
Ensuite, toujours dans <literal>hibernate.cfg.xml</literal>, lier les permissions aux rôles :
Ensuite, toujours dans <literal>hibernate.cfg.xml</literal>, lier les permissions aux rôles :
</para>
<programlisting><![CDATA[<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>]]></programlisting>
<para>
Les noms de rôle sont les rôles compris par votre fournisseur JAAC.
Les noms de rôle sont les rôles compris par votre fournisseur JAAC.
</para>
</sect1>

View File

@ -1,19 +1,6 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
~ Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, v. 2.1. This program is distributed in the
~ hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
~ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details. You should have received a
~ copy of the GNU Lesser General Public License, v.2.1 along with this
~ distribution; if not, write to the Free Software Foundation, Inc.,
~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
~
~ Red Hat Author(s): Steve Ebersole
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="example-mappings">
<title>Exemple : quelques mappings</title>
@ -22,14 +9,14 @@
</para>
<sect1 id="example-mappings-emp">
<title>Employeur/Employé (Employer/Employee)</title>
<title>Employeur/Employé (Employer/Employee)</title>
<para>
Le modèle suivant de relation entre <literal>Employer</literal> et
<literal>Employee</literal> utilise une vraie classe entité (<literal>Employment</literal>)
pour représenter l'association. On a fait cela parce qu'il peut y avoir plus d'une période
d'emploi pour les deux mêmes parties. Des composants sont utilisés pour modéliser les
valeurs monétaires et les noms des employés.
Le modèle suivant de relation entre <literal>Employer</literal> et
<literal>Employee</literal> utilise une vraie classe entité (<literal>Employment</literal>)
pour représenter l'association. On a fait cela parce qu'il peut y avoir plus d'une période
d'emploi pour les deux mêmes parties. Des composants sont utilisés pour modéliser les
valeurs monétaires et les noms des employés.
</para>
<mediaobject>
@ -95,7 +82,7 @@
</hibernate-mapping>]]></programlisting>
<para>
Et voici le schéma des tables générées par <literal>SchemaExport</literal>.
Et voici le schéma des tables générées par <literal>SchemaExport</literal>.
</para>
<programlisting><![CDATA[create table employers (
@ -138,12 +125,12 @@ create sequence employer_id_seq]]></programlisting>
<title>Auteur/Travail (Author/Work)</title>
<para>
Soit le modèle de la relation entre <literal>Work</literal>, <literal>Author</literal>
et <literal>Person</literal>. Nous représentons la relation entre <literal>Work</literal>
Soit le modèle de la relation entre <literal>Work</literal>, <literal>Author</literal>
et <literal>Person</literal>. Nous représentons la relation entre <literal>Work</literal>
et <literal>Author</literal> comme une association plusieurs-vers-plusieurs. Nous avons choisi de
représenter la relation entre <literal>Author</literal> et <literal>Person</literal>
comme une association un-vers-un. Une autre possibilité aurait été que
<literal>Author</literal> hérite de <literal>Person</literal>.
représenter la relation entre <literal>Author</literal> et <literal>Person</literal>
comme une association un-vers-un. Une autre possibilité aurait été que
<literal>Author</literal> hérite de <literal>Person</literal>.
</para>
<mediaobject>
@ -156,7 +143,7 @@ create sequence employer_id_seq]]></programlisting>
</mediaobject>
<para>
Le mapping suivant représente exactement ces relations :
Le mapping suivant représente exactement ces relations :
</para>
<programlisting><![CDATA[<hibernate-mapping>
@ -214,9 +201,9 @@ create sequence employer_id_seq]]></programlisting>
<para>
Il y a quatre tables dans ce mapping. <literal>works</literal>,
<literal>authors</literal> et <literal>persons</literal> qui contiennent
respectivement les données de work, author et person.
respectivement les données de work, author et person.
<literal>author_work</literal> est une table d'association qui lie authors
à works. Voici le schéma de tables, généré par <literal>SchemaExport</literal>.
à works. Voici le schéma de tables, généré par <literal>SchemaExport</literal>.
</para>
<programlisting><![CDATA[create table works (
@ -260,14 +247,14 @@ alter table author_work
<title>Client/Commande/Produit (Customer/Order/Product)</title>
<para>
Imaginons maintenant le modèle de relation entre <literal>Customer</literal>,
Imaginons maintenant le modèle de relation entre <literal>Customer</literal>,
<literal>Order</literal>, <literal>LineItem</literal> et <literal>Product</literal>.
Il y a une association un-vers-plusieurs entre <literal>Customer</literal> et
<literal>Order</literal>, mais comment devrions nous représenter <literal>Order</literal> /
<literal>Order</literal>, mais comment devrions nous représenter <literal>Order</literal> /
<literal>LineItem</literal> / <literal>Product</literal>? J'ai choisi de mapper
<literal>LineItem</literal> comme une classe d'association représentant l'association
<literal>LineItem</literal> comme une classe d'association représentant l'association
plusieurs-vers-plusieurs entre <literal>Order</literal> et <literal>Product</literal>. Dans
Hibernate, on appelle cela un élément composite.
Hibernate, on appelle cela un élément composite.
</para>
<mediaobject>
@ -323,8 +310,8 @@ alter table author_work
<para>
<literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> et
<literal>products</literal> contiennent les données de customer, order, order line item et product.
<literal>line_items</literal> est aussi la table d'association liant orders à products.
<literal>products</literal> contiennent les données de customer, order, order line item et product.
<literal>line_items</literal> est aussi la table d'association liant orders à products.
</para>
<programlisting><![CDATA[create table customers (
@ -404,7 +391,7 @@ alter table line_items
</sect2>
<sect2 id="example-mappings-composite-key">
<title>Exemple de clef composée</title>
<title>Exemple de clef composée</title>
<programlisting><![CDATA[<class name="Customer">
<id name="customerId"
@ -521,7 +508,7 @@ alter table line_items
</sect2>
<sect2 id="example-mappings-composite-key-manytomany">
<title>Many-to-many avec une clef composée partagée</title>
<title>Many-to-many avec une clef composée partagée</title>
<programlisting><![CDATA[<class name="User" table="`User`">
<composite-id>
<key-property name="name"/>
@ -560,7 +547,7 @@ alter table line_items
</sect2>
<sect2 id="example-mappings-content-discrimination">
<title>Contenu basé sur une discrimination</title>
<title>Contenu basé sur une discrimination</title>
<programlisting><![CDATA[<class name="Person"
discriminator-value="P">
@ -614,7 +601,7 @@ alter table line_items
</sect2>
<sect2 id="example-mappings-association-alternatekeys" revision="2">
<title>Associations sur des clefs alternées</title>
<title>Associations sur des clefs alternées</title>
<programlisting><![CDATA[<class name="Person">
<id name="id">

View File

@ -1,61 +1,63 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="example-parentchild">
<title>Exemple : Père/Fils</title>
<title>Exemple : Père/Fils</title>
<para>
L'une des premières choses que les nouveaux utilisateurs essaient de faire avec Hibernate est de modéliser
une relation père/fils. Il y a deux approches différentes pour cela. Pour un certain nombre de raisons, la méthode la
plus courante, en particulier pour les nouveaux utilisateurs, est de modéliser les deux relations <literal>Père</literal>
et <literal>Fils</literal> comme des classes entités liées par une association <literal>&lt;one-to-many&gt;</literal> du
<literal>Père</literal> vers le <literal>Fils</literal> (l'autre approche est de déclarer le <literal>Fils</literal>
comme un <literal>&lt;composite-element&gt;</literal>). Il est évident que le sens de l'association un vers plusieurs
(dans Hibernate) est bien moins proche du sens habituel d'une relation père/fils que ne l'est celui d'un
élément cmposite. Nous allons vous expliquer comment utiliser une association <emphasis>un vers plusieurs bidirectionnelle
avec cascade</emphasis> afin de modéliser efficacement et élégamment une relation père/fils, ce n'est vraiment
L'une des premières choses que les nouveaux utilisateurs essaient de faire avec Hibernate est de modéliser
une relation père/fils. Il y a deux approches différentes pour cela. Pour un certain nombre de raisons, la méthode la
plus courante, en particulier pour les nouveaux utilisateurs, est de modéliser les deux relations <literal>Père</literal>
et <literal>Fils</literal> comme des classes entités liées par une association <literal>&lt;one-to-many&gt;</literal> du
<literal>Père</literal> vers le <literal>Fils</literal> (l'autre approche est de déclarer le <literal>Fils</literal>
comme un <literal>&lt;composite-element&gt;</literal>). Il est évident que le sens de l'association un vers plusieurs
(dans Hibernate) est bien moins proche du sens habituel d'une relation père/fils que ne l'est celui d'un
élément cmposite. Nous allons vous expliquer comment utiliser une association <emphasis>un vers plusieurs bidirectionnelle
avec cascade</emphasis> afin de modéliser efficacement et élégamment une relation père/fils, ce n'est vraiment
pas difficile !
</para>
<sect1 id="example-parentchild-collections">
<title>Une note à propos des collections</title>
<title>Une note à propos des collections</title>
<para>
Les collections Hibernate sont considérées comme étant une partie logique
de l'entité dans laquelle elles sont contenues ; jamais des entités qu'elle
contient. C'est une distinction crutiale ! Les conséquences sont les suivantes :
Les collections Hibernate sont considérées comme étant une partie logique
de l'entité dans laquelle elles sont contenues ; jamais des entités qu'elle
contient. C'est une distinction crutiale ! Les conséquences sont les suivantes :
</para>
<itemizedlist>
<listitem>
<para>
Quand nous ajoutons / retirons un objet d'une collection, le numéro de version du
propriétaire de la collection est incrémenté.
Quand nous ajoutons / retirons un objet d'une collection, le numéro de version du
propriétaire de la collection est incrémenté.
</para>
</listitem>
<listitem>
<para>
Si un objet qui a été enlevé d'une collection est une instance de type valeur (ex :
élément composite), cet objet cessera d'être persistant et son état sera complètement effacé
de la base de données. Par ailleurs, ajouter une instance de type valeur dans une collection
aura pour conséquence que son état sera immédiatement persistant.
Si un objet qui a été enlevé d'une collection est une instance de type valeur (ex :
élément composite), cet objet cessera d'être persistant et son état sera complètement effacé
de la base de données. Par ailleurs, ajouter une instance de type valeur dans une collection
aura pour conséquence que son état sera immédiatement persistant.
</para>
</listitem>
<listitem>
<para>
Si une entité est enlevée d'une collection (association un-vers-plusieurs
ou plusieurs-vers-plusieurs), par défaut, elle ne sera pas effacée. Ce comportement
est complètement logique - une modification de l'un des états internes d'une entité
ne doit pas causer la disparition de l'entité associée !
De même, l'ajout d'une entité dans une collection n'engendre pas,
par défaut, la persistance de cette entité.
Si une entité est enlevée d'une collection (association un-vers-plusieurs
ou plusieurs-vers-plusieurs), par défaut, elle ne sera pas effacée. Ce comportement
est complètement logique - une modification de l'un des états internes d'une entité
ne doit pas causer la disparition de l'entité associée !
De même, l'ajout d'une entité dans une collection n'engendre pas,
par défaut, la persistance de cette entité.
</para>
</listitem>
</itemizedlist>
<para>
Le comportement par défaut est donc que l'ajout d'une entité dans une collection créé
simplement le lien entre les deux entités, et qu'effacer une entité supprime ce lien.
C'est le comportement le plus approprié dans la plupart des cas. Ce comportement n'est
cependant pas approprié lorsque la vie du fils est liée au cycle de vie du père.
Le comportement par défaut est donc que l'ajout d'une entité dans une collection créé
simplement le lien entre les deux entités, et qu'effacer une entité supprime ce lien.
C'est le comportement le plus approprié dans la plupart des cas. Ce comportement n'est
cependant pas approprié lorsque la vie du fils est liée au cycle de vie du père.
</para>
</sect1>
@ -84,16 +86,16 @@ session.save(c);
session.flush();]]></programlisting>
<para>
Hibernate exécuterait deux ordres SQL:
Hibernate exécuterait deux ordres SQL:
</para>
<itemizedlist>
<listitem>
<para>un <literal>INSERT</literal> pour créer l'enregistrement pour <literal>c</literal></para>
<para>un <literal>INSERT</literal> pour créer l'enregistrement pour <literal>c</literal></para>
</listitem>
<listitem>
<para>
un <literal>UPDATE</literal> pour créer le lien de <literal>p</literal> vers
un <literal>UPDATE</literal> pour créer le lien de <literal>p</literal> vers
<literal>c</literal>
</para>
</listitem>
@ -101,8 +103,8 @@ session.flush();]]></programlisting>
<para>
Ceci est non seuleument inefficace, mais viole aussi toute contrainte <literal>NOT NULL</literal> sur
la colonne <literal>parent_id</literal>. Nous pouvons réparer la contrainte de nullité
en spécifiant <literal>not-null="true"</literal> dans le mapping de la collection :
la colonne <literal>parent_id</literal>. Nous pouvons réparer la contrainte de nullité
en spécifiant <literal>not-null="true"</literal> dans le mapping de la collection :
</para>
<programlisting><![CDATA[<set name="children">
@ -111,25 +113,25 @@ session.flush();]]></programlisting>
</set>]]></programlisting>
<para>
Cependant ce n'est pas la solution recommandée.
Cependant ce n'est pas la solution recommandée.
</para>
<para>
La cause sous jacente à ce comportement est que le lien (la clé étrangère <literal>parent_id</literal>) de
<literal>p</literal> vers <literal>c</literal> n'est pas considérée comme faisant partie de l'état
de l'objet <literal>Child</literal> et n'est donc pas créé par l'<literal>INSERT</literal>.
La cause sous jacente à ce comportement est que le lien (la clé étrangère <literal>parent_id</literal>) de
<literal>p</literal> vers <literal>c</literal> n'est pas considérée comme faisant partie de l'état
de l'objet <literal>Child</literal> et n'est donc pas créé par l'<literal>INSERT</literal>.
La solution est donc que ce lien fasse partie du mapping de <literal>Child</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="parent" column="parent_id" not-null="true"/>]]></programlisting>
<para>
(Nous avons aussi besoin d'ajouter la propriété <literal>parent</literal> dans la classe <literal>Child</literal>).
(Nous avons aussi besoin d'ajouter la propriété <literal>parent</literal> dans la classe <literal>Child</literal>).
</para>
<para>
Maintenant que l'état du lien est géré par l'entité <literal>Child</literal>, nous spécifions à la
collection de ne pas mettre à jour le lien. Nous utilisons l'attribut <literal>inverse</literal>.
Maintenant que l'état du lien est géré par l'entité <literal>Child</literal>, nous spécifions à la
collection de ne pas mettre à jour le lien. Nous utilisons l'attribut <literal>inverse</literal>.
</para>
<programlisting><![CDATA[<set name="children" inverse="true">
@ -138,7 +140,7 @@ session.flush();]]></programlisting>
</set>]]></programlisting>
<para>
Le code suivant serait utilisé pour ajouter un nouveau <literal>Child</literal>
Le code suivant serait utilisé pour ajouter un nouveau <literal>Child</literal>
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
@ -149,11 +151,11 @@ session.save(c);
session.flush();]]></programlisting>
<para>
Maintenant, seul un <literal>INSERT</literal> SQL est nécessaire !
Maintenant, seul un <literal>INSERT</literal> SQL est nécessaire !
</para>
<para>
Pour alléger encore un peu les choses, nous devrions créer une méthode <literal>addChild()</literal>
Pour alléger encore un peu les choses, nous devrions créer une méthode <literal>addChild()</literal>
dans <literal>Parent</literal>.
</para>
@ -188,7 +190,7 @@ session.flush();]]></programlisting>
</set>]]></programlisting>
<para>
Simplifie le code précédent en
Simplifie le code précédent en
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
@ -197,9 +199,9 @@ p.addChild(c);
session.flush();]]></programlisting>
<para>
De la même manière, nous n'avons pas à itérer sur les fils lorsque nous sauvons
De la même manière, nous n'avons pas à itérer sur les fils lorsque nous sauvons
ou effacons un <literal>Parent</literal>. Le code suivant efface <literal>p</literal>
et tous ses fils de la base de données.
et tous ses fils de la base de données.
</para>
<programlisting><![CDATA[Parent p = (Parent) session.load(Parent.class, pid);
@ -217,7 +219,7 @@ c.setParent(null);
session.flush();]]></programlisting>
<para>
n'effacera pas <literal>c</literal> de la base de données, il enlèvera seulement
n'effacera pas <literal>c</literal> de la base de données, il enlèvera seulement
le lien vers <literal>p</literal> (et causera une violation de contrainte
<literal>NOT NULL</literal>, dans ce cas).
Vous devez explicitement utiliser <literal>delete()</literal> sur <literal>Child</literal>.
@ -230,8 +232,8 @@ session.delete(c);
session.flush();]]></programlisting>
<para>
Dans notre cas, un <literal>Child</literal> ne peut pas vraiment exister sans son père. Si nous
effacons un <literal>Child</literal> de la collection, nous voulons vraiment qu'il soit effacé.
Dans notre cas, un <literal>Child</literal> ne peut pas vraiment exister sans son père. Si nous
effacons un <literal>Child</literal> de la collection, nous voulons vraiment qu'il soit effacé.
Pour cela, nous devons utiliser <literal>cascade="all-delete-orphan"</literal>.
</para>
@ -241,9 +243,9 @@ session.flush();]]></programlisting>
</set>]]></programlisting>
<para>
A noter : même si le mapping de la collection spécifie <literal>inverse="true"</literal>, les cascades
sont toujours assurées par l'itération sur les éléments de la collection. Donc, si vous avez besoin
qu'un objet soit enregistré, effacé ou mis à jour par cascade, vous devez l'ajouter dans la colleciton.
A noter : même si le mapping de la collection spécifie <literal>inverse="true"</literal>, les cascades
sont toujours assurées par l'itération sur les éléments de la collection. Donc, si vous avez besoin
qu'un objet soit enregistré, effacé ou mis à jour par cascade, vous devez l'ajouter dans la colleciton.
Il ne suffit pas d'appeler explicitement <literal>setParent()</literal>.
</para>
@ -253,26 +255,26 @@ session.flush();]]></programlisting>
<title>Cascades et <literal>unsaved-value</literal></title>
<para>
Supposons que nous ayons chargé un <literal>Parent</literal> dans une <literal>Session</literal>,
que nous l'ayons ensuite modifié et que voulions persiter ces modifications dans une nouvelle session
Supposons que nous ayons chargé un <literal>Parent</literal> dans une <literal>Session</literal>,
que nous l'ayons ensuite modifié et que voulions persiter ces modifications dans une nouvelle session
en appelant <literal>update()</literal>.
Le <literal>Parent</literal> contiendra une collection de fils et, puisque la cascade est activée,
Hibernate a besoin de savoir quels fils viennent d'être instanciés et quels fils proviennent de la base
de données. Supposons aussi que <literal>Parent</literal> et <literal>Child</literal> ont tous deux
Le <literal>Parent</literal> contiendra une collection de fils et, puisque la cascade est activée,
Hibernate a besoin de savoir quels fils viennent d'être instanciés et quels fils proviennent de la base
de données. Supposons aussi que <literal>Parent</literal> et <literal>Child</literal> ont tous deux
des identifiants du type <literal>Long</literal>.
Hibernate utilisera la propriété de l'identifiant et la propriété de la version/horodatage pour déterminer quels fils sont nouveaux
(vous pouvez aussi utiliser la propriété version ou timestamp, voir
Hibernate utilisera la propriété de l'identifiant et la propriété de la version/horodatage pour déterminer quels fils sont nouveaux
(vous pouvez aussi utiliser la propriété version ou timestamp, voir
<xref linkend="manipulatingdata-updating-detached"/>).
<emphasis>Dans Hibernate3, il n'est plus nécessaire de spécifier
<emphasis>Dans Hibernate3, il n'est plus nécessaire de spécifier
une <literal>unsaved-value</literal> explicitement.</emphasis>
</para>
<para>
Le code suivant mettra à jour <literal>parent</literal> et <literal>child</literal>
et insérera <literal>newChild</literal>.
Le code suivant mettra à jour <literal>parent</literal> et <literal>child</literal>
et insérera <literal>newChild</literal>.
</para>
<programlisting><![CDATA[//parent et child ont été chargés dans une session précédente
<programlisting><![CDATA[//parent et child ont été chargés dans une session précédente
parent.addChild(child);
Child newChild = new Child();
parent.addChild(newChild);
@ -280,12 +282,12 @@ session.update(parent);
session.flush();]]></programlisting>
<para>
Ceci est très bien pour des identifiants générés, mais qu'en est-il des identifiants assignés et des
identifiants composés ? C'est plus difficile,
puisqu'Hibernate ne peut pas utiliser la propriété de l'identifiant pour distinguer un objet
nouvellement instancié (avec un identifiant assigné par l'utilisateur) d'un objet chargé dans une session précédente.
Dans ce cas, Hibernate utilisera soit la propriété de version ou d'horodatage, soit effectuera vraiment une requête au cache
de second niveau, soit, dans le pire des cas, à la base de données, pour voir si la ligne existe.
Ceci est très bien pour des identifiants générés, mais qu'en est-il des identifiants assignés et des
identifiants composés ? C'est plus difficile,
puisqu'Hibernate ne peut pas utiliser la propriété de l'identifiant pour distinguer un objet
nouvellement instancié (avec un identifiant assigné par l'utilisateur) d'un objet chargé dans une session précédente.
Dans ce cas, Hibernate utilisera soit la propriété de version ou d'horodatage, soit effectuera vraiment une requête au cache
de second niveau, soit, dans le pire des cas, à la base de données, pour voir si la ligne existe.
</para>
<!-- undocumenting
@ -354,17 +356,17 @@ public boolean onSave(Object entity,
<title>Conclusion</title>
<para>
Il y a quelques principes à maîtriser dans ce chapitre et tout cela peut paraître déroutant la première fois.
Il y a quelques principes à maîtriser dans ce chapitre et tout cela peut paraître déroutant la première fois.
Cependant, dans la pratique, tout fonctionne parfaitement. La plupart des applications Hibernate utilisent
le pattern père / fils.
le pattern père / fils.
</para>
<para>
Nous avons évoqué une alternative dans le premier paragraphe. Aucun des points traités précédemment n'existe
dans le cas d'un mapping <literal>&lt;composite-element&gt;</literal> qui possède exactement la sémantique
d'une relation père / fils. Malheureusement, il y a deux grandes limitations pour les classes éléments
composites : les éléments composites ne peuvent contenir de collections, et ils ne peuvent être les fils
d'entités autres que l'unique parent.
Nous avons évoqué une alternative dans le premier paragraphe. Aucun des points traités précédemment n'existe
dans le cas d'un mapping <literal>&lt;composite-element&gt;</literal> qui possède exactement la sémantique
d'une relation père / fils. Malheureusement, il y a deux grandes limitations pour les classes éléments
composites : les éléments composites ne peuvent contenir de collections, et ils ne peuvent être les fils
d'entités autres que l'unique parent.
</para>
</sect1>

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="example-weblog">
<title>Exemple : application Weblog</title>
@ -6,9 +8,9 @@
<title>Classes persistantes</title>
<para>
Les classes persistantes representent un weblog, et un article posté
dans un weblog. Il seront modélisés comme une relation père/fils
standard, mais nous allons utiliser un "bag" trié au lieu d'un set.
Les classes persistantes representent un weblog, et un article posté
dans un weblog. Il seront modélisés comme une relation père/fils
standard, mais nous allons utiliser un "bag" trié au lieu d'un set.
</para>
@ -91,7 +93,7 @@ public class BlogItem {
<title>Mappings Hibernate</title>
<para>
Le mapping XML doit maintenant être relativement simple à vos yeux.
Le mapping XML doit maintenant être relativement simple à vos yeux.
</para>
<programlisting><![CDATA[<?xml version="1.0"?>

View File

@ -1,32 +1,34 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="filters">
<title>Filtrer les données</title>
<title>Filtrer les données</title>
<para>
Hibernate3 fournit une nouvelle approche innovatrice pour gérer des données
avec des règles de "visibilité". Un <emphasis>filtre Hibernate</emphasis> est un filtre
global, nommé, paramétré qui peut être activé ou désactivé pour une session Hibernate
particulière.
Hibernate3 fournit une nouvelle approche innovatrice pour gérer des données
avec des règles de "visibilité". Un <emphasis>filtre Hibernate</emphasis> est un filtre
global, nommé, paramétré qui peut être activé ou désactivé pour une session Hibernate
particulière.
</para>
<sect1 id="objectstate-filters">
<title>Filtres Hibernate</title>
<para>
Hibernate3 ajoute la capacité de prédéfinir des critères de filtre et d'attacher ces
filtres à une classe ou à une collection. Un critère de filtre est la faculté de définir
une clause de restriction très similaire à l'attribut "where" existant disponible sur
une classe et divers éléments d'une collection. Mis à part que ces conditions de filtre
peuvent être paramétrées. L'application peut alors prendre la décision à l'exécution
si des filtres donnés devraient être activés et quels devraient être leurs paramètres.
Des filtres peuvent être utilisés comme des vues de base de données, mais paramétrées
Hibernate3 ajoute la capacité de prédéfinir des critères de filtre et d'attacher ces
filtres à une classe ou à une collection. Un critère de filtre est la faculté de définir
une clause de restriction très similaire à l'attribut "where" existant disponible sur
une classe et divers éléments d'une collection. Mis à part que ces conditions de filtre
peuvent être paramétrées. L'application peut alors prendre la décision à l'exécution
si des filtres donnés devraient être activés et quels devraient être leurs paramètres.
Des filtres peuvent être utilisés comme des vues de base de données, mais paramétrées
dans l'application.
</para>
<para>
Afin d'utiliser des filtres, ils doivent d'abord être définis, puis attachés aux éléments
de mapping appropriés. Pour définir un filtre, utilisez l'élément <literal>&lt;filter-def/&gt;</literal>
dans un élément <literal>&lt;hibernate-mapping/&gt;</literal> :
Afin d'utiliser des filtres, ils doivent d'abord être définis, puis attachés aux éléments
de mapping appropriés. Pour définir un filtre, utilisez l'élément <literal>&lt;filter-def/&gt;</literal>
dans un élément <literal>&lt;hibernate-mapping/&gt;</literal> :
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
@ -34,7 +36,7 @@
</filter-def>]]></programlisting>
<para>
Puis, ce filtre peut être attaché à une classe :
Puis, ce filtre peut être attaché à une classe :
</para>
<programlisting><![CDATA[<class name="myClass" ...>
@ -43,7 +45,7 @@
</class>]]></programlisting>
<para>
ou à une collection :
ou à une collection :
</para>
<programlisting><![CDATA[<set ...>
@ -51,27 +53,27 @@
</set>]]></programlisting>
<para>
ou même aux deux (ou à plusieurs de chaque) en même temps.
ou même aux deux (ou à plusieurs de chaque) en même temps.
</para>
<para>
Les méthodes sur <literal>Session</literal> sont : <literal>enableFilter(String filterName)</literal>,
Les méthodes sur <literal>Session</literal> sont : <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, et <literal>disableFilter(String filterName)</literal>.
Par défaut, les filtres <emphasis>ne sont pas</emphasis> activés pour une session donnée ;
ils doivent être explicitement activés en appelant la méthode
Par défaut, les filtres <emphasis>ne sont pas</emphasis> activés pour une session donnée ;
ils doivent être explicitement activés en appelant la méthode
<literal>Session.enabledFilter()</literal>, laquelle retourne une instance de l'interface
<literal>Filter</literal>. Utiliser le simple filtre défini au-dessus ressemblerait à :
<literal>Filter</literal>. Utiliser le simple filtre défini au-dessus ressemblerait à :
</para>
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
<para>
Notez que des méthodes sur l'interface org.hibernate.Filter autorisent le chaînage de beaucoup
de méthodes communes d'Hibernate.
Notez que des méthodes sur l'interface org.hibernate.Filter autorisent le chaînage de beaucoup
de méthodes communes d'Hibernate.
</para>
<para>
Un exemple complet, utilisant des données temporelles avec une structure de date
Un exemple complet, utilisant des données temporelles avec une structure de date
d'enregistrement effectif :
</para>
@ -104,9 +106,9 @@
</class>]]></programlisting>
<para>
Puis, afin de s'assurer que vous pouvez toujours récupérer les enregistrements actuellement
effectifs, activez simplement le filtre sur la session avant de récupérer des données des
employés :
Puis, afin de s'assurer que vous pouvez toujours récupérer les enregistrements actuellement
effectifs, activez simplement le filtre sur la session avant de récupérer des données des
employés :
</para>
<programlisting><![CDATA[Session session = ...;
@ -117,16 +119,16 @@ List results = session.createQuery("from Employee as e where e.salary > :targetS
]]></programlisting>
<para>
Dans le HQL ci-dessus, bien que nous ayons seulement mentionné une contrainte de
salaire sur les resultats, à cause du filtre activé, la requête retournera seulement
les employés actuellement actifs qui ont un salaire supérieur à un million de dollars.
Dans le HQL ci-dessus, bien que nous ayons seulement mentionné une contrainte de
salaire sur les resultats, à cause du filtre activé, la requête retournera seulement
les employés actuellement actifs qui ont un salaire supérieur à un million de dollars.
</para>
<para>
A noter : si vous prévoyez d'utiliser des filtres avec des jointures externes (soit
à travers HQL, soit par le chargement) faites attention à la direction de l'expression
de condition. Il est plus sûr de la positionner pour les jointures externes à gauche ;
en général, placez le paramètre d'abord, suivi du(des) nom(s) de colonne après l'opérateur.
A noter : si vous prévoyez d'utiliser des filtres avec des jointures externes (soit
à travers HQL, soit par le chargement) faites attention à la direction de l'expression
de condition. Il est plus sûr de la positionner pour les jointures externes à gauche ;
en général, placez le paramètre d'abord, suivi du(des) nom(s) de colonne après l'opérateur.
</para>
</sect1>

View File

@ -1,18 +1,20 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="inheritance">
<title>Mapping d'héritage de classe</title>
<title>Mapping d'héritage de classe</title>
<sect1 id="inheritance-strategies" revision="3">
<title>Les trois stratégies</title>
<title>Les trois stratégies</title>
<para>
Hibernate supporte les trois stratégies d'héritage de base :
Hibernate supporte les trois stratégies d'héritage de base :
</para>
<itemizedlist>
<listitem>
<para>
une table par hiérarchie de classe (table per class hierarchy)
une table par hiérarchie de classe (table per class hierarchy)
</para>
</listitem>
<listitem>
@ -22,13 +24,13 @@
</listitem>
<listitem>
<para>
une table par classe concrète (table per concrete class)
une table par classe concrète (table per concrete class)
</para>
</listitem>
</itemizedlist>
<para>
Hibernate supporte en plus une quatrièmestratégie, légèrement différente, qui supporte le polymorphisme :
Hibernate supporte en plus une quatrièmestratégie, légèrement différente, qui supporte le polymorphisme :
</para>
<itemizedlist>
@ -40,26 +42,26 @@
</itemizedlist>
<para>
Il est possible d'utiliser différentes stratégies de mapping pour différentes branches d'une même
hiérarchie d'héritage, et alors d'employer le polymorphisme implicite pour réaliser le
polymorphisme à travers toute la hiérarchie. Pourtant, Hibernate ne supporte pas de mélanger
Il est possible d'utiliser différentes stratégies de mapping pour différentes branches d'une même
hiérarchie d'héritage, et alors d'employer le polymorphisme implicite pour réaliser le
polymorphisme à travers toute la hiérarchie. Pourtant, Hibernate ne supporte pas de mélanger
des mappings <literal>&lt;subclass&gt;</literal> et
<literal>&lt;joined-subclass&gt;</literal> et <literal>&lt;union-subclass&gt;</literal>
pour le même élément <literal>&lt;class&gt;</literal> racine.
Il est possible de mélanger ensemble les stratégies d'une table par hiérarchie et d'une
table par sous-classe, pour le même élément <literal>&lt;class&gt;</literal>, en combinant
les éléments <literal>&lt;subclass&gt;</literal> et <literal>&lt;join&gt;</literal> (voir dessous).
pour le même élément <literal>&lt;class&gt;</literal> racine.
Il est possible de mélanger ensemble les stratégies d'une table par hiérarchie et d'une
table par sous-classe, pour le même élément <literal>&lt;class&gt;</literal>, en combinant
les éléments <literal>&lt;subclass&gt;</literal> et <literal>&lt;join&gt;</literal> (voir dessous).
</para>
<para>
Il est possible de définir des mappings de <literal>subclass</literal>, <literal>union-subclass</literal>,
et <literal>joined-subclass</literal> dans des documents de mapping séparés, directement sous
<literal>hibernate-mapping</literal>. Ceci vous permet d'étendre une hiérarchie de classe juste en
ajoutant un nouveau fichier de mapping. Vous devez spécifier un attribut <literal>extends</literal>
dans le mapping de la sous-classe, en nommant une super-classe précédemment mappée. Note :
précédemment cette foncionnalité rendait l'ordre des documents de mapping important. Depuis
Il est possible de définir des mappings de <literal>subclass</literal>, <literal>union-subclass</literal>,
et <literal>joined-subclass</literal> dans des documents de mapping séparés, directement sous
<literal>hibernate-mapping</literal>. Ceci vous permet d'étendre une hiérarchie de classe juste en
ajoutant un nouveau fichier de mapping. Vous devez spécifier un attribut <literal>extends</literal>
dans le mapping de la sous-classe, en nommant une super-classe précédemment mappée. Note :
précédemment cette foncionnalité rendait l'ordre des documents de mapping important. Depuis
Hibernate3, l'ordre des fichier de mapping n'importe plus lors de l'utilisation du mot-clef "extends".
L'ordre à l'intérieur d'un simple fichier de mapping impose encore de définir les classes mères
L'ordre à l'intérieur d'un simple fichier de mapping impose encore de définir les classes mères
avant les classes filles.
</para>
@ -72,12 +74,12 @@
<sect2 id="inheritance-tableperclass" >
<title>Une table par hiérarchie de classe</title>
<title>Une table par hiérarchie de classe</title>
<para>
Supposons que nous ayons une interface <literal>Payment</literal>, implémentée
Supposons que nous ayons une interface <literal>Payment</literal>, implémentée
par <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
<literal>ChequePayment</literal>. La stratégie une table par hiérarchie serait :
<literal>ChequePayment</literal>. La stratégie une table par hiérarchie serait :
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
@ -101,7 +103,7 @@
<para>
Une seule table est requise. Une grande limitation de cette
stratégie est que les colonnes déclarées par les classes filles, telles que <literal>CCTYPE</literal>,
stratégie est que les colonnes déclarées par les classes filles, telles que <literal>CCTYPE</literal>,
ne peuvent avoir de contrainte <literal>NOT NULL</literal>.
</para>
@ -111,7 +113,7 @@
<title>Une table par classe fille</title>
<para>
La stratégie une table par classe fille serait :
La stratégie une table par classe fille serait :
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
@ -137,7 +139,7 @@
<para>
Quatre tables sont requises. Les trois tables des classes filles ont
une clé primaire associée à la table classe mère (le modèle relationnel
une clé primaire associée à la table classe mère (le modèle relationnel
est une association un-vers-un).
</para>
@ -147,14 +149,14 @@
<title>Une table par classe fille, en utilisant un discriminant</title>
<para>
Notez que l'implémentation Hibernate de la stratégie un table par
classe fille ne nécessite pas de colonne discriminante dans la table
classe mère. D'autres implémentations de mappers Objet/Relationnel utilisent
une autre implémentation de la stratégie une table par classe fille qui nécessite
une colonne de type discriminant dans la table de la classe mère. L'approche
prise par Hibernate est plus difficile à implémenter mais plus correcte
Notez que l'implémentation Hibernate de la stratégie un table par
classe fille ne nécessite pas de colonne discriminante dans la table
classe mère. D'autres implémentations de mappers Objet/Relationnel utilisent
une autre implémentation de la stratégie une table par classe fille qui nécessite
une colonne de type discriminant dans la table de la classe mère. L'approche
prise par Hibernate est plus difficile à implémenter mais plus correcte
d'une point de vue relationnel. Si vous aimeriez utiliser
une colonne discriminante avec la stratégie d'une table par classe fille, vous pourriez combiner
une colonne discriminante avec la stratégie d'une table par classe fille, vous pourriez combiner
l'utilisation de <literal>&lt;subclass&gt;</literal> et
<literal>&lt;join&gt;</literal>, comme suit :
</para>
@ -188,17 +190,17 @@
</class>]]></programlisting>
<para>
La déclaration optionnelle <literal>fetch="select"</literal> indique à Hibernate
de ne pas récupérer les données de la classe fille <literal>ChequePayment</literal> par une jointure externe lors des requêtes sur la classe mère.
La déclaration optionnelle <literal>fetch="select"</literal> indique à Hibernate
de ne pas récupérer les données de la classe fille <literal>ChequePayment</literal> par une jointure externe lors des requêtes sur la classe mère.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>Mélange d'une table par hiérarchie de classe avec une table par classe fille</title>
<title>Mélange d'une table par hiérarchie de classe avec une table par classe fille</title>
<para>
Vous pouvez même mélanger les stratégies d'une table par hiérarchie de classe et d'une table par classe fille en utilisant cette approche :
Vous pouvez même mélanger les stratégies d'une table par hiérarchie de classe et d'une table par classe fille en utilisant cette approche :
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
@ -223,8 +225,8 @@
</class>]]></programlisting>
<para>
Pour importe laquelle de ces stratégies, une association polymorphique vers la classe racine
<literal>Payment</literal> est mappée en utilisant <literal>&lt;many-to-one&gt;</literal>.
Pour importe laquelle de ces stratégies, une association polymorphique vers la classe racine
<literal>Payment</literal> est mappée en utilisant <literal>&lt;many-to-one&gt;</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
@ -232,10 +234,10 @@
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="2">
<title>Une table par classe concrète</title>
<title>Une table par classe concrète</title>
<para>
Il y a deux manières d'utiliser la stratégie d'une table par classe concrète. La première
Il y a deux manières d'utiliser la stratégie d'une table par classe concrète. La première
est d'employer <literal>&lt;union-subclass&gt;</literal>.
</para>
@ -258,30 +260,30 @@
</class>]]></programlisting>
<para>
Trois tables sont nécessaires pour les classes filles. Chaque table définit des colonnes
pour toutes les propriétés de la classe, incluant les propriétés héritéés.
Trois tables sont nécessaires pour les classes filles. Chaque table définit des colonnes
pour toutes les propriétés de la classe, incluant les propriétés héritéés.
</para>
<para>
La limitation de cette approche est que si une propriété est mappée sur la classe mère, le nom
de la colonne doit être le même pour toutes les classes filles. (Nous pourrions être plus souple
La limitation de cette approche est que si une propriété est mappée sur la classe mère, le nom
de la colonne doit être le même pour toutes les classes filles. (Nous pourrions être plus souple
dans une future version d'Hibernate).
La stratégie du générateur d'identifiant n'est pas permise dans l'héritage de classes filles par
La stratégie du générateur d'identifiant n'est pas permise dans l'héritage de classes filles par
union, en effet la valeur (NdT : seed) de la clef primaire
doit être partagée par toutes les classes filles "union" d'une hiérarchie.
doit être partagée par toutes les classes filles "union" d'une hiérarchie.
</para>
<para>
Si votre classe mère est abstraite, mappez la avec <literal>abstract="true"</literal>.
Bien sûr, si elle n'est pas abstraite, une table supplémentaire (par défaut,
Si votre classe mère est abstraite, mappez la avec <literal>abstract="true"</literal>.
Bien sûr, si elle n'est pas abstraite, une table supplémentaire (par défaut,
<literal>PAYMENT</literal> dans l'exemple ci-dessus) est requise pour contenir des instances
de la classe mère.
de la classe mère.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Une table par classe concrète, en utilisant le polymorphisme implicite</title>
<title>Une table par classe concrète, en utilisant le polymorphisme implicite</title>
<para>
Une approche alternative est l'emploi du polymorphisme implicite :
@ -313,20 +315,20 @@
<para>
Notez que nulle part nous ne mentionnons l'interface <literal>Payment</literal> explicitement.
Notez aussi que des propriétés de <literal>Payment</literal> sont mappées dans
chaque classe fille. Si vous voulez éviter des duplications, considérez l'utilisation des
entités XML (cf. <literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
dans la déclaration du <literal>DOCTYPE</literal> et <literal>&amp;allproperties;</literal> dans le mapping).
Notez aussi que des propriétés de <literal>Payment</literal> sont mappées dans
chaque classe fille. Si vous voulez éviter des duplications, considérez l'utilisation des
entités XML (cf. <literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
dans la déclaration du <literal>DOCTYPE</literal> et <literal>&amp;allproperties;</literal> dans le mapping).
</para>
<para>
L'inconvénient de cette approche est qu'Hibernate ne génère pas d'<literal>UNION</literal>s SQL
lors de l'exécution des requêtes polymorphiques.
L'inconvénient de cette approche est qu'Hibernate ne génère pas d'<literal>UNION</literal>s SQL
lors de l'exécution des requêtes polymorphiques.
</para>
<para>
Pour cette stratégie de mapping, une association polymorphique pour <literal>Payment</literal>
est habituellement mappée en utilisant <literal>&lt;any&gt;</literal>.
Pour cette stratégie de mapping, une association polymorphique pour <literal>Payment</literal>
est habituellement mappée en utilisant <literal>&lt;any&gt;</literal>.
</para>
<programlisting><![CDATA[<any name="payment" meta-type="string" id-type="long">
@ -340,14 +342,14 @@
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>Mélange du polymorphisme implicite avec d'autres mappings d'héritage</title>
<title>Mélange du polymorphisme implicite avec d'autres mappings d'héritage</title>
<para>
Il y a une chose supplémentaire à noter à propos de ce mapping. Puisque les classes filles sont
chacune mappées avec leur propre élément <literal>&lt;class&gt;</literal> (et puisque
Il y a une chose supplémentaire à noter à propos de ce mapping. Puisque les classes filles sont
chacune mappées avec leur propre élément <literal>&lt;class&gt;</literal> (et puisque
<literal>Payment</literal> est juste une interface), chaque classe fille pourrait
facilement faire partie d'une autre hiérarchie
d'héritage ! (Et vous pouvez encore faire des requêtes polymorphiques pour l'interface <literal>Payment</literal>).
facilement faire partie d'une autre hiérarchie
d'héritage ! (Et vous pouvez encore faire des requêtes polymorphiques pour l'interface <literal>Payment</literal>).
</para>
<programlisting><![CDATA[<class name="CreditCardPayment" table="CREDIT_PAYMENT">
@ -380,10 +382,10 @@
<para>
Encore une fois, nous ne mentionnons pas explicitement <literal>Payment</literal>.
Si nous exécutons une requête sur l'interface <literal>Payment</literal> - par
Si nous exécutons une requête sur l'interface <literal>Payment</literal> - par
exemple, <literal>from Payment</literal> - Hibernate retournera
automatiquement les instances de <literal>CreditCardPayment</literal>
(et ses classes filles puisqu'elles implémentent aussi <literal>Payment</literal>),
(et ses classes filles puisqu'elles implémentent aussi <literal>Payment</literal>),
<literal>CashPayment</literal> et <literal>ChequePayment</literal> mais pas
les instances de <literal>NonelectronicTransaction</literal>.
</para>
@ -396,17 +398,17 @@
<title>Limitations</title>
<para>
Il y a certaines limitations à l'approche du "polymorphisme implicite"
pour la stratégie de mapping d'une table par classe concrète.
Il y a plutôt moins de limitations restrictives aux mappings <literal>&lt;union-subclass&gt;</literal>.
Il y a certaines limitations à l'approche du "polymorphisme implicite"
pour la stratégie de mapping d'une table par classe concrète.
Il y a plutôt moins de limitations restrictives aux mappings <literal>&lt;union-subclass&gt;</literal>.
</para>
<para>
La table suivante montre les limitations des mappings d'une table par classe concrète, et du polymorphisme implicite, dans Hibernate.
La table suivante montre les limitations des mappings d'une table par classe concrète, et du polymorphisme implicite, dans Hibernate.
</para>
<table frame="topbot">
<title>Caractéristiques du mapping d'héritage</title>
<title>Caractéristiques du mapping d'héritage</title>
<tgroup cols='8' align='left' colsep='1' rowsep='1'>
<colspec colname='c1' colwidth="1*"/>
<colspec colname='c2' colwidth="1*"/>
@ -418,20 +420,20 @@
<colspec colname='c8' colwidth="1*"/>
<thead>
<row>
<entry>Stratégie d'héritage</entry>
<entry>Stratégie d'héritage</entry>
<entry>many-to-one polymorphique</entry>
<entry>one-to-one polymorphique</entry>
<entry>one-to-many polymorphique</entry>
<entry>many-to-many polymorphique</entry>
<entry><literal>load()/get()</literal> polymorphique</entry>
<entry>Requêtes polymorphiques</entry>
<entry>Requêtes polymorphiques</entry>
<entry>Jointures polymorphiques</entry>
<entry>Récupération par jointure externe</entry>
<entry>Récupération par jointure externe</entry>
</row>
</thead>
<tbody>
<row>
<entry>une table par hiérarchie de classe</entry>
<entry>une table par hiérarchie de classe</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
@ -439,7 +441,7 @@
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supportée</emphasis></entry>
<entry><emphasis>supportée</emphasis></entry>
</row>
<row>
<entry>une table par classe fille</entry>
@ -450,10 +452,10 @@
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supportée</emphasis></entry>
<entry><emphasis>supportée</emphasis></entry>
</row>
<row>
<entry>une table par classe concrète (union-subclass)</entry>
<entry>une table par classe concrète (union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (pour <literal>inverse="true"</literal> seulement)</entry>
@ -461,18 +463,18 @@
<entry><literal>s.get(Payment.class, id)</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><literal>from Order o join o.payment p</literal></entry>
<entry><emphasis>supportée</emphasis></entry>
<entry><emphasis>supportée</emphasis></entry>
</row>
<row>
<entry>une table par classe concrète (polymorphisme implicite)</entry>
<entry>une table par classe concrète (polymorphisme implicite)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>non supporté</emphasis></entry>
<entry><emphasis>non supporté</emphasis></entry>
<entry><emphasis>non supporté</emphasis></entry>
<entry><emphasis>non supporté</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>non supportées</emphasis></entry>
<entry><emphasis>non supportée</emphasis></entry>
<entry><emphasis>non supportées</emphasis></entry>
<entry><emphasis>non supportée</emphasis></entry>
</row>
</tbody>
</tgroup>

View File

@ -1,20 +1,22 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="persistent-classes" revision="2">
<title>Classes persistantes</title>
<para>
Les classes persistantes sont les classes d'une application qui implémentent
les entités d'un problème métier (ex. Client et Commande dans une application
de commerce électronique).
Toutes les instances d'une classe persistante ne sont pas forcément
dans l'état persistant - au lieu de cela, une instance peut être éphémère (NdT : transient) ou détachée.
Les classes persistantes sont les classes d'une application qui implémentent
les entités d'un problème métier (ex. Client et Commande dans une application
de commerce électronique).
Toutes les instances d'une classe persistante ne sont pas forcément
dans l'état persistant - au lieu de cela, une instance peut être éphémère (NdT : transient) ou détachée.
</para>
<para>
Hibernate fonctionne de manière optimale lorsque ces classes suivent quelques règles
simples, aussi connues comme le modèle de programmation Plain Old Java Object
(POJO). Cependant, aucune de ces règles ne sont des besoins absolus. En effet, Hibernate3 suppose très peu de choses à propos
de la nature de vos objets persistants. Vous pouvez exprimer un modèle de domaine par d'autres moyens : utiliser des arbres
Hibernate fonctionne de manière optimale lorsque ces classes suivent quelques règles
simples, aussi connues comme le modèle de programmation Plain Old Java Object
(POJO). Cependant, aucune de ces règles ne sont des besoins absolus. En effet, Hibernate3 suppose très peu de choses à propos
de la nature de vos objets persistants. Vous pouvez exprimer un modèle de domaine par d'autres moyens : utiliser des arbres
d'instances de <literal>Map</literal>, par exemple.
</para>
@ -22,8 +24,8 @@
<title>Un exemple simple de POJO</title>
<para>
Toute bonne application Java nécessite une classe persistante
représentant les félins.
Toute bonne application Java nécessite une classe persistante
représentant les félins.
</para>
<programlisting><![CDATA[package eg;
@ -106,49 +108,49 @@ public class Cat {
}]]></programlisting>
<para>
Il y a quatre règles à suivre ici :
Il y a quatre règles à suivre ici :
</para>
<sect2 id="persistent-classes-pojo-constructor" revision="1">
<title>Implémenter un constructeur sans argument</title>
<title>Implémenter un constructeur sans argument</title>
<para>
<literal>Cat</literal> a un constructeur sans argument. Toutes les classes persistantes doivent avoir un
constructeur par défaut (lequel peut ne pas être public) pour qu'Hibernate puissent les instancier en utilisant
<literal>Constructor.newInstance()</literal>. Nous recommandons fortement d'avoir un constructeur par défaut avec
au moins une visibilité <emphasis>paquet</emphasis> pour la génération du proxy à l'exécution dans Hibernate.
constructeur par défaut (lequel peut ne pas être public) pour qu'Hibernate puissent les instancier en utilisant
<literal>Constructor.newInstance()</literal>. Nous recommandons fortement d'avoir un constructeur par défaut avec
au moins une visibilité <emphasis>paquet</emphasis> pour la génération du proxy à l'exécution dans Hibernate.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-identifier" revision="2">
<title>Fournir une propriété d'indentifiant (optionnel)</title>
<title>Fournir une propriété d'indentifiant (optionnel)</title>
<para>
<literal>Cat</literal> possède une propriété appelée <literal>id</literal>.
Cette propriété mappe la valeur de la colonne de clé primaire de la table
d'une base de données.La propriété aurait pu s'appeler complètement autrement,
et son type aurait pu être n'importe quel type primitif, n'importe quel "encapsuleur"
<literal>Cat</literal> possède une propriété appelée <literal>id</literal>.
Cette propriété mappe la valeur de la colonne de clé primaire de la table
d'une base de données.La propriété aurait pu s'appeler complètement autrement,
et son type aurait pu être n'importe quel type primitif, n'importe quel "encapsuleur"
de type primitif, <literal>java.lang.String</literal> ou <literal>java.util.Date</literal>.
(Si votre base de données héritée possède des clés composites, elles peuvent être mappées
en utilisant une classe définie par l'utilisateur et possédant les propriétés associées aux
types de la clé composite - voir la section concernant les identifiants composites plus tard).
(Si votre base de données héritée possède des clés composites, elles peuvent être mappées
en utilisant une classe définie par l'utilisateur et possédant les propriétés associées aux
types de la clé composite - voir la section concernant les identifiants composites plus tard).
</para>
<para>
La propriété d'identifiant est strictement optionnelle. Vous pouver l'oublier et laisser Hibernate
La propriété d'identifiant est strictement optionnelle. Vous pouver l'oublier et laisser Hibernate
s'occuper des identifiants de l'objet en interne. Toutefois, nous ne le recommandons pas.
</para>
<para>
En fait, quelques fonctionnalités ne sont disponibles que pour les classes
déclarant un identifiant de propriété :
En fait, quelques fonctionnalités ne sont disponibles que pour les classes
déclarant un identifiant de propriété :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Les réattachements transitifs pour les objets détachés (mise à jour en cascade ou fusion en cascade) -
Les réattachements transitifs pour les objets détachés (mise à jour en cascade ou fusion en cascade) -
voir <xref linkend="objectstate-transitive"/>
</para>
</listitem>
@ -165,8 +167,8 @@ public class Cat {
</itemizedlist>
<para>
Nous recommandons que vous déclariez les propriétés d'identifiant de manière
uniforme. Nous recommandons également que vous utilisiez un type nullable
Nous recommandons que vous déclariez les propriétés d'identifiant de manière
uniforme. Nous recommandons également que vous utilisiez un type nullable
(ie. non primitif).
</para>
</sect2>
@ -174,39 +176,39 @@ public class Cat {
<sect2 id="persistent-classes-pojo-final">
<title>Favoriser les classes non finales (optionnel)</title>
<para>
Une fonctionnalité clef d'Hibernate, les <emphasis>proxies</emphasis>, nécessitent
que la classe persistente soit non finale ou qu'elle soit l'implémentation d'une
interface qui déclare toutes les méthodes publiques.
Une fonctionnalité clef d'Hibernate, les <emphasis>proxies</emphasis>, nécessitent
que la classe persistente soit non finale ou qu'elle soit l'implémentation d'une
interface qui déclare toutes les méthodes publiques.
</para>
<para>
Vous pouvez persister, grâce à Hibernate, les classes <literal>final</literal>
qui n'implémentent pas d'interface, mais vous ne pourrez pas utiliser les proxies pour les chargements d'associations paresseuses
- ce qui limitera vos possibilités d'ajustement des performances.
Vous pouvez persister, grâce à Hibernate, les classes <literal>final</literal>
qui n'implémentent pas d'interface, mais vous ne pourrez pas utiliser les proxies pour les chargements d'associations paresseuses
- ce qui limitera vos possibilités d'ajustement des performances.
</para>
<para>
Vous devriez aussi éviter de déclarer des méthodes <literal>public final</literal> sur des classes
non-finales. Si vous voulez utiliser une classe avec une méthode <literal>public final</literal>, vous devez
explicitement désactiver les proxies en paramétrant
Vous devriez aussi éviter de déclarer des méthodes <literal>public final</literal> sur des classes
non-finales. Si vous voulez utiliser une classe avec une méthode <literal>public final</literal>, vous devez
explicitement désactiver les proxies en paramétrant
<literal>lazy="false"</literal>.
</para>
</sect2>
<sect2 id="persistent-classes-pojo-accessors" revision="2">
<title>Déclarer les accesseurs et mutateurs des attributs persistants (optionnel)</title>
<title>Déclarer les accesseurs et mutateurs des attributs persistants (optionnel)</title>
<para>
<literal>Cat</literal> déclare des mutateurs pour toutes ses champs persistants. Beaucoup d'autres
<literal>Cat</literal> déclare des mutateurs pour toutes ses champs persistants. Beaucoup d'autres
solutions de mapping Objet/relationnel persistent directement les variables d'instance. Nous pensons
qu'il est bien mieux de fournir une indirection entre le schéma relationnel et les structures de données internes de la classe.
Par défaut, Hibernate persiste les propriétés suivant le style JavaBean, et reconnaît les noms de méthodes de la forme <literal>
qu'il est bien mieux de fournir une indirection entre le schéma relationnel et les structures de données internes de la classe.
Par défaut, Hibernate persiste les propriétés suivant le style JavaBean, et reconnaît les noms de méthodes de la forme <literal>
getFoo</literal>, <literal>isFoo</literal> et
<literal>setFoo</literal>. Nous pouvons changer pour un accès direct aux champs pour des propriétés particulières, si besoin est.
<literal>setFoo</literal>. Nous pouvons changer pour un accès direct aux champs pour des propriétés particulières, si besoin est.
</para>
<para>
Les propriétés <emphasis>n'ont pas</emphasis> à être déclarées publiques -
Hibernate peut persister une propriété avec un paire de getter/setter de
visibilité par défault, <literal>protected</literal> ou <literal>
Les propriétés <emphasis>n'ont pas</emphasis> à être déclarées publiques -
Hibernate peut persister une propriété avec un paire de getter/setter de
visibilité par défault, <literal>protected</literal> ou <literal>
private</literal>.
</para>
@ -215,11 +217,11 @@ public class Cat {
</sect1>
<sect1 id="persistent-classes-inheritance">
<title>Implémenter l'héritage</title>
<title>Implémenter l'héritage</title>
<para>
Une sous-classe doit également suivre la première et la seconde règle.
Elle hérite sa propriété d'identifiant de <literal>Cat</literal>.
Une sous-classe doit également suivre la première et la seconde règle.
Elle hérite sa propriété d'identifiant de <literal>Cat</literal>.
</para>
<programlisting><![CDATA[package eg;
@ -237,55 +239,55 @@ public class DomesticCat extends Cat {
</sect1>
<sect1 id="persistent-classes-equalshashcode" revision="1">
<title>Implémenter <literal>equals()</literal> et <literal>hashCode()</literal></title>
<title>Implémenter <literal>equals()</literal> et <literal>hashCode()</literal></title>
<para>
Vous devez surcharger les méthodes <literal>equals()</literal> et
Vous devez surcharger les méthodes <literal>equals()</literal> et
<literal>hashCode()</literal> si vous
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
avez l'intention de mettre des instances de classes persistantes dans un <literal>Set</literal>
(la manière recommandée pour représenter des associations pluri-valuées)
(la manière recommandée pour représenter des associations pluri-valuées)
<emphasis>et</emphasis>
</para>
</listitem>
<listitem>
<para>
avez l'intention d'utiliser le réattachement d'instances détachées
avez l'intention d'utiliser le réattachement d'instances détachées
</para>
</listitem>
</itemizedlist>
<para>
Hibernate garantit l'équivalence de l'identité persistante (ligne de base de données) et l'identité Java seulement
à l'intérieur de la portée d'une session particulière. Donc dès que nous mélangeons des instances venant de différentes
sessions, nous devons implémenter <literal>equals()</literal> et
<literal>hashCode()</literal> si nous souhaitons avoir une sémantique correcte pour les <literal>Set</literal>s.
Hibernate garantit l'équivalence de l'identité persistante (ligne de base de données) et l'identité Java seulement
à l'intérieur de la portée d'une session particulière. Donc dès que nous mélangeons des instances venant de différentes
sessions, nous devons implémenter <literal>equals()</literal> et
<literal>hashCode()</literal> si nous souhaitons avoir une sémantique correcte pour les <literal>Set</literal>s.
</para>
<para>
La manière la plus évidente est d'implémenter <literal>equals()</literal>/<literal>hashCode()</literal>
La manière la plus évidente est d'implémenter <literal>equals()</literal>/<literal>hashCode()</literal>
en comparant la valeur de l'identifiant des deux objets. Si cette valeur est identique, les deux
doivent représenter la même ligne de base de données, ils sont donc égaux (si les deux sont
ajoutés à un <literal>Set</literal>, nous n'aurons qu'un seul élément dans le
doivent représenter la même ligne de base de données, ils sont donc égaux (si les deux sont
ajoutés à un <literal>Set</literal>, nous n'aurons qu'un seul élément dans le
<literal>Set</literal>). Malheureusement, nous ne pouvons pas utiliser cette approche avec
des identifiants générés ! Hibernate n'assignera de
valeur d'identifiant qu'aux objets qui sont persistants, une instance nouvellement créée n'aura
donc pas de valeur d'identifiant ! De plus, si une instance est non sauvegardée et actuellement dans un <literal>Set</literal>,
le sauvegarder assignera une valeur d'identifiant à l'objet. Si <literal>equals()</literal> et <literal>hashCode()</literal>
sont basées sur la valeur de l'identifiant, le code de hachage devrait changer, rompant le contrat du <literal>Set</literal>.
Regardez sur le site web d'Hibernate pour une discussion complète de ce problème.
Notez que ceci n'est pas un problème d'Hibernate, mais la sémantique normale de Java pour l'identité d'un objet et l'égalité.
des identifiants générés ! Hibernate n'assignera de
valeur d'identifiant qu'aux objets qui sont persistants, une instance nouvellement créée n'aura
donc pas de valeur d'identifiant ! De plus, si une instance est non sauvegardée et actuellement dans un <literal>Set</literal>,
le sauvegarder assignera une valeur d'identifiant à l'objet. Si <literal>equals()</literal> et <literal>hashCode()</literal>
sont basées sur la valeur de l'identifiant, le code de hachage devrait changer, rompant le contrat du <literal>Set</literal>.
Regardez sur le site web d'Hibernate pour une discussion complète de ce problème.
Notez que ceci n'est pas un problème d'Hibernate, mais la sémantique normale de Java pour l'identité d'un objet et l'égalité.
</para>
<para>
Nous recommandons donc d'implémenter
Nous recommandons donc d'implémenter
<literal>equals()</literal> et <literal>hashCode()</literal> en utilisant <emphasis>
l'égalité par clé métier</emphasis>.L'égalité par clé métier signifie que la méthode <literal>equals()</literal>
compare uniquement les propriétés qui forment une clé métier, une clé qui
identifierait notre instance dans le monde réel (une clé candidate
l'égalité par clé métier</emphasis>.L'égalité par clé métier signifie que la méthode <literal>equals()</literal>
compare uniquement les propriétés qui forment une clé métier, une clé qui
identifierait notre instance dans le monde réel (une clé candidate
<emphasis>naturelle</emphasis>) :
</para>
@ -314,39 +316,39 @@ public class DomesticCat extends Cat {
}]]></programlisting>
<para>
Notez qu'une clef métier ne doit pas être solide comme une clef primaire de base de données
(voir <xref linkend="transactions-basics-identity"/>). Les propriétés
immuables ou uniques sont généralement de bonnes candidates pour une clef métier.
Notez qu'une clef métier ne doit pas être solide comme une clef primaire de base de données
(voir <xref linkend="transactions-basics-identity"/>). Les propriétés
immuables ou uniques sont généralement de bonnes candidates pour une clef métier.
</para>
</sect1>
<sect1 id="persistent-classes-dynamicmodels">
<title>Modèles dynamiques</title>
<title>Modèles dynamiques</title>
<para>
<emphasis>Notez que la fonctionnalités suivantes sont actuellement considérées
comme expérimentales et peuvent changer dans un futur proche.</emphasis>
<emphasis>Notez que la fonctionnalités suivantes sont actuellement considérées
comme expérimentales et peuvent changer dans un futur proche.</emphasis>
</para>
<para>
Les entités persistantes ne doivent pas nécessairement être représentées comme
des classes POJO ou des objets JavaBean à l'exécution. Hibernate supporte aussi les
modèles dynamiques (en utilisant des <literal>Map</literal>s de <literal>Map</literal>s
à l'exécution) et la représentation des entités comme des arbres DOM4J. Avec cette
approche, vous n'écrivez pas de classes persistantes, seulement des fichiers de mapping.
Les entités persistantes ne doivent pas nécessairement être représentées comme
des classes POJO ou des objets JavaBean à l'exécution. Hibernate supporte aussi les
modèles dynamiques (en utilisant des <literal>Map</literal>s de <literal>Map</literal>s
à l'exécution) et la représentation des entités comme des arbres DOM4J. Avec cette
approche, vous n'écrivez pas de classes persistantes, seulement des fichiers de mapping.
</para>
<para>
Par défaut, Hibernate fonctionne en mode POJO normal. Vous pouvez paramétrer
un mode de représentation d'entité par défaut pour une <literal>SessionFactory</literal>
particulière en utilisant l'option de configuration <literal>default_entity_mode</literal>
Par défaut, Hibernate fonctionne en mode POJO normal. Vous pouvez paramétrer
un mode de représentation d'entité par défaut pour une <literal>SessionFactory</literal>
particulière en utilisant l'option de configuration <literal>default_entity_mode</literal>
(voir <xref linkend="configuration-optional-properties"/>).
</para>
<para>
Les exemples suivants démontrent la représentation utilisant des <literal>Map</literal>s.
D'abord, dans le fichier de mapping, un <literal>entity-name</literal> doit être déclaré
Les exemples suivants démontrent la représentation utilisant des <literal>Map</literal>s.
D'abord, dans le fichier de mapping, un <literal>entity-name</literal> doit être déclaré
au lieu (ou en plus) d'un nom de classe :
</para>
@ -385,13 +387,13 @@ public class DomesticCat extends Cat {
</hibernate-mapping>]]></programlisting>
<para>
Notez que même si des associations sont déclarées en utilisant des noms de classe cible,
le type de cible d'une association peut aussi être une entité dynamique au lieu d'un POJO.
Notez que même si des associations sont déclarées en utilisant des noms de classe cible,
le type de cible d'une association peut aussi être une entité dynamique au lieu d'un POJO.
</para>
<para>
Après avoir configuré le mode d'entité par défaut à <literal>dynamic-map</literal>
pour la <literal>SessionFactory</literal>, nous pouvons lors de l'exécution fonctionner
Après avoir configuré le mode d'entité par défaut à <literal>dynamic-map</literal>
pour la <literal>SessionFactory</literal>, nous pouvons lors de l'exécution fonctionner
avec des <literal>Map</literal>s de <literal>Map</literal>s :
</para>
@ -419,15 +421,15 @@ s.close();]]></programlisting>
<para>
Les avantages d'un mapping dynamique sont un gain de temps pour le prototypage
sans la nécessité d'implémenter les classes d'entité. Pourtant, vous perdez la
vérification du typage au moment de la compilation et aurez plus d'exceptions à
gérer lors de l'exécution. Grâce au mapping d'Hibernate, le schéma de la base de
données peut facilement être normalisé et solidifié, permettant de rajouter une
implémentation propre du modèle de domaine plus tard.
sans la nécessité d'implémenter les classes d'entité. Pourtant, vous perdez la
vérification du typage au moment de la compilation et aurez plus d'exceptions à
gérer lors de l'exécution. Grâce au mapping d'Hibernate, le schéma de la base de
données peut facilement être normalisé et solidifié, permettant de rajouter une
implémentation propre du modèle de domaine plus tard.
</para>
<para>
Les modes de représentation d'une entité peut aussi être configuré par <literal>Session</literal> :
Les modes de représentation d'une entité peut aussi être configuré par <literal>Session</literal> :
</para>
<programlisting><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
@ -445,17 +447,17 @@ dynamicSession.close()
<para>
Veuillez noter que l'appel à <literal>getSession()</literal> en utilisant un
Veuillez noter que l'appel à <literal>getSession()</literal> en utilisant un
<literal>EntityMode</literal> se fait sur l'API <literal>Session</literal>, pas
<literal>SessionFactory</literal>. De cette manière, la nouvelle <literal>Session</literal>
<literal>SessionFactory</literal>. De cette manière, la nouvelle <literal>Session</literal>
partage les connexions JDBC, transactions et autres informations de contexte sous-jacentes.
Cela signifie que vous n'avez pas à appeler <literal>flush()</literal> et <literal>close()</literal>
Cela signifie que vous n'avez pas à appeler <literal>flush()</literal> et <literal>close()</literal>
sur la <literal>Session</literal> secondaire, et laissez aussi la gestion de la transaction
et de la connexion à l'unité de travail primaire.
et de la connexion à l'unité de travail primaire.
</para>
<para>
Plus d'informations à propos de la représentation XML peuvent être trouvées dans
Plus d'informations à propos de la représentation XML peuvent être trouvées dans
<xref linkend="xml"/>.
</para>
@ -466,27 +468,27 @@ dynamicSession.close()
<para>
<literal>org.hibernate.tuple.Tuplizer</literal>, et ses sous-interfaces, sont responsables
de la gestion d'une représentation particulière d'un morceau de données, en fonction du
<literal>org.hibernate.EntityMode</literal> de réprésentation. Si un morceau donné de données
est pensé comme une structure de données, alors un tuplizer est la chose qui sait comment
créer une telle structure de données, comment extraire des valeurs et injecter des valeurs dans
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
de la gestion d'une représentation particulière d'un morceau de données, en fonction du
<literal>org.hibernate.EntityMode</literal> de réprésentation. Si un morceau donné de données
est pensé comme une structure de données, alors un tuplizer est la chose qui sait comment
créer une telle structure de données, comment extraire des valeurs et injecter des valeurs dans
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
<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
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.
</para>
<para>
Les utilisateurs peuvent aussi brancher leurs propres tuplizers. Peut-être vous est-il nécessaire qu'une
implémentation de <literal>java.util.Map</literal> autre que <literal>java.util.HashMap</literal>
soit utilisée dans le mode d'entité dynamic-map ; ou peut-être avez-vous besoin de définir une
statégie de génération de proxy différente de celle utilisée par défaut. Les deux devraient être
effectuées en définissant une implémentation de tuplizer utilisateur. Les définitions de tuplizers
sont attachées au mapping de l'entité ou du composant qu'ils sont censés gérer. Retour à l'exemple de
notre entité utilisateur :
Les utilisateurs peuvent aussi brancher leurs propres tuplizers. Peut-être vous est-il nécessaire qu'une
implémentation de <literal>java.util.Map</literal> autre que <literal>java.util.HashMap</literal>
soit utilisée dans le mode d'entité dynamic-map ; ou peut-être avez-vous besoin de définir une
statégie de génération de proxy différente de celle utilisée par défaut. Les deux devraient être
effectuées en définissant une implémentation de tuplizer utilisateur. Les définitions de tuplizers
sont attachées au mapping de l'entité ou du composant qu'ils sont censés gérer. Retour à l'exemple de
notre entité utilisateur :
</para>
<programlisting><![CDATA[<hibernate-mapping>

View File

@ -1,5 +1,4 @@
<?xml version='1.0'?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<preface id="preface">

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="querycriteria">
<title>Requêtes par critères</title>
<title>Requêtes par critères</title>
<para>
Hibernate offre une API d'interrogation par critères intuitive et extensible.
Hibernate offre une API d'interrogation par critères intuitive et extensible.
</para>
<sect1 id="querycriteria-creating">
<title>Créer une instance de <literal>Criteria</literal></title>
<title>Créer une instance de <literal>Criteria</literal></title>
<para>
L'interface <literal>net.sf.hibernate.Criteria</literal> représente une requête sur une
classe persistente donnée. La <literal>Session</literal> fournit les instances de
L'interface <literal>net.sf.hibernate.Criteria</literal> représente une requête sur une
classe persistente donnée. La <literal>Session</literal> fournit les instances de
<literal>Criteria</literal>.
</para>
@ -22,14 +24,14 @@ List cats = crit.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-narrowing">
<title>Restriction du résultat</title>
<title>Restriction du résultat</title>
<para>
Un criterion (critère de recherche) est une instance de l'interface
Un criterion (critère de recherche) est une instance de l'interface
<literal>org.hibernate.criterion.Criterion</literal>. La classe
<literal>org.hibernate.criterion.Restrictions</literal> définit
des méthodes pour obtenir des types de <literal>Criterion</literal>
pré-définis.
<literal>org.hibernate.criterion.Restrictions</literal> définit
des méthodes pour obtenir des types de <literal>Criterion</literal>
pré-définis.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
@ -38,7 +40,7 @@ List cats = crit.list();]]></programlisting>
.list();]]></programlisting>
<para>
Les restrictions peuvent être goupées de manière logique.
Les restrictions peuvent être goupées de manière logique.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
@ -60,8 +62,8 @@ List cats = crit.list();]]></programlisting>
.list();]]></programlisting>
<para>
Il y a plusieurs types de criterion pré-définis (sous classes de <literal>Restriction</literal>),
mais l'une d'entre elle particulièrement utile vous permet de spécifier directement
Il y a plusieurs types de criterion pré-définis (sous classes de <literal>Restriction</literal>),
mais l'une d'entre elle particulièrement utile vous permet de spécifier directement
du SQL.
</para>
@ -70,13 +72,13 @@ List cats = crit.list();]]></programlisting>
.list();]]></programlisting>
<para>
La zone <literal>{alias}</literal> sera remplacée par l'alias de colonne de l'entité
que l'on souhaite intérroger.
La zone <literal>{alias}</literal> sera remplacée par l'alias de colonne de l'entité
que l'on souhaite intérroger.
</para>
<para>
Une autre approche pour obtenir un criterion est de le récupérer d'une instance de <literal>Property</literal>.
Vous pouvez créer une <literal>Property</literal> en appelant <literal>Property.forName()</literal>.
Une autre approche pour obtenir un criterion est de le récupérer d'une instance de <literal>Property</literal>.
Vous pouvez créer une <literal>Property</literal> en appelant <literal>Property.forName()</literal>.
</para>
<programlisting><![CDATA[
@ -94,10 +96,10 @@ List cats = sess.createCriteria(Cat.class)
</sect1>
<sect1 id="querycriteria-ordering">
<title>Trier les résultats</title>
<title>Trier les résultats</title>
<para>
Vous pouvez trier les résultats en utilisant <literal>org.hibernate.criterion.Order</literal>.
Vous pouvez trier les résultats en utilisant <literal>org.hibernate.criterion.Order</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
@ -120,7 +122,7 @@ List cats = sess.createCriteria(Cat.class)
<title>Associations</title>
<para>
Vous pouvez facilement spécifier des contraintes sur des entités liées,
Vous pouvez facilement spécifier des contraintes sur des entités liées,
par des associations en utilisant <literal>createCriteria()</literal>.
</para>
@ -132,7 +134,7 @@ List cats = sess.createCriteria(Cat.class)
<para>
Notez que la seconde <literal>createCriteria()</literal> retourne une nouvelle
instance de <literal>Criteria</literal>, qui se rapporte aux éléments de la
instance de <literal>Criteria</literal>, qui se rapporte aux éléments de la
collection <literal>kittens</literal>.
</para>
@ -147,14 +149,14 @@ List cats = sess.createCriteria(Cat.class)
.list();]]></programlisting>
<para>
(<literal>createAlias()</literal> ne crée pas de nouvelle instance de
(<literal>createAlias()</literal> ne crée pas de nouvelle instance de
<literal>Criteria</literal>.)
</para>
<para>
Notez que les collections kittens contenues dans les instances de <literal>Cat</literal>
retournées par les deux précédentes requêtes ne sont <emphasis>pas</emphasis> pré-filtrées
par les critères ! Si vous souhaitez récupérer uniquement les kittens qui correspondent à la
retournées par les deux précédentes requêtes ne sont <emphasis>pas</emphasis> pré-filtrées
par les critères ! Si vous souhaitez récupérer uniquement les kittens qui correspondent à la
criteria, vous devez utiliser <literal>ResultTransformer</literal>.
</para>
@ -173,11 +175,11 @@ while ( iter.hasNext() ) {
</sect1>
<sect1 id="querycriteria-dynamicfetching" revision="1">
<title>Peuplement d'associations de manière dynamique</title>
<title>Peuplement d'associations de manière dynamique</title>
<para>
Vous pouvez spéficier au moment de l'exécution le peuplement d'une association en utilisant
<literal>setFetchMode()</literal> (c'est-à-dire le chargement de celle-ci).
Vous pouvez spéficier au moment de l'exécution le peuplement d'une association en utilisant
<literal>setFetchMode()</literal> (c'est-à-dire le chargement de celle-ci).
Cela permet de surcharger les valeurs
"lazy" et "outer-join" du mapping.
</para>
@ -189,18 +191,18 @@ while ( iter.hasNext() ) {
.list();]]></programlisting>
<para>
Cette requête recherchera <literal>mate</literal> et <literal>kittens</literal>
Cette requête recherchera <literal>mate</literal> et <literal>kittens</literal>
via les jointures externes. Voir <xref linkend="performance-fetching"/> pour plus d'informations.
</para>
</sect1>
<sect1 id="querycriteria-examples">
<title>Requêtes par l'exemple</title>
<title>Requêtes par l'exemple</title>
<para>
La classe <literal>org.hibernate.criterion.Example</literal> vous permet de
construire un critère suivant une instance d'objet donnée.
construire un critère suivant une instance d'objet donnée.
</para>
<programlisting><![CDATA[Cat cat = new Cat();
@ -211,12 +213,12 @@ List results = session.createCriteria(Cat.class)
.list();]]></programlisting>
<para>
Les propriétés de type version, identifiant et association sont ignorées.
Par défaut, les valeurs null sont exclues.
Les propriétés de type version, identifiant et association sont ignorées.
Par défaut, les valeurs null sont exclues.
</para>
<para>
Vous pouvez ajuster la stratégie d'utilisation de valeurs de
Vous pouvez ajuster la stratégie d'utilisation de valeurs de
l'<literal>Exemple</literal>.
</para>
@ -230,7 +232,7 @@ List results = session.createCriteria(Cat.class)
.list();]]></programlisting>
<para>
Vous pouvez utiliser les "exemples" pour des critères sur les objets associés.
Vous pouvez utiliser les "exemples" pour des critères sur les objets associés.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
@ -242,11 +244,11 @@ List results = session.createCriteria(Cat.class)
</sect1>
<sect1 id="querycriteria-projection">
<title>Projections, agrégation et regroupement</title>
<title>Projections, agrégation et regroupement</title>
<para>
La classe <literal>org.hibernate.criterion.Projections</literal> est une
fabrique d'instances de <literal>Projection</literal>. Nous appliquons une
projection sur une requête en appelant <literal>setProjection()</literal>.
projection sur une requête en appelant <literal>setProjection()</literal>.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
@ -264,16 +266,16 @@ List results = session.createCriteria(Cat.class)
.list();]]></programlisting>
<para>
Il n'y a pas besoin de "group by" explicite dans une requête par critère.
Certains types de projection sont définis pour être des <emphasis>projections
Il n'y a pas besoin de "group by" explicite dans une requête par critère.
Certains types de projection sont définis pour être des <emphasis>projections
de regroupement</emphasis>, lesquels apparaissent aussi dans la clause
<literal>group by</literal> SQL.
</para>
<para>
Un alias peut optionnellement être assigné à une projection, ainsi la valeur
projetée peut être référencée dans des restrictions ou des tris. Voici deux façons
différentes de faire ça :
Un alias peut optionnellement être assigné à une projection, ainsi la valeur
projetée peut être référencée dans des restrictions ou des tris. Voici deux façons
différentes de faire ça :
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
@ -287,9 +289,9 @@ List results = session.createCriteria(Cat.class)
.list();]]></programlisting>
<para>
Les méthodes <literal>alias()</literal> et <literal>as()</literal> enveloppe simplement
une instance de projection dans une autre instance (aliasée) de <literal>Projection</literal>.
Comme un raccourci, vous pouvez assignez un alias lorsque vous ajoutez la projection à la
Les méthodes <literal>alias()</literal> et <literal>as()</literal> enveloppe simplement
une instance de projection dans une autre instance (aliasée) de <literal>Projection</literal>.
Comme un raccourci, vous pouvez assignez un alias lorsque vous ajoutez la projection à la
liste de projections :
</para>
@ -337,10 +339,10 @@ List results = session.createCriteria(Cat.class)
</sect1>
<sect1 id="querycriteria-detachedqueries">
<title>Requêtes et sous-requêtes détachées</title>
<title>Requêtes et sous-requêtes détachées</title>
<para>
La classe <literal>DetachedCriteria</literal> vous laisse créer une requête en dehors de la
portée de la session, et puis l'exécuter plus tard en utilisant n'importe quelle <literal>Session</literal>
La classe <literal>DetachedCriteria</literal> vous laisse créer une requête en dehors de la
portée de la session, et puis l'exécuter plus tard en utilisant n'importe quelle <literal>Session</literal>
arbitraire.
</para>
@ -354,8 +356,8 @@ txn.commit();
session.close();]]></programlisting>
<para>
Une <literal>DetachedCriteria</literal> peut aussi être utilisée pour exprimer une
sous-requête. Des instances de criterion impliquant des sous-requêtes peuvent être
Une <literal>DetachedCriteria</literal> peut aussi être utilisée pour exprimer une
sous-requête. Des instances de criterion impliquant des sous-requêtes peuvent être
obtenues via <literal>Subqueries</literal> ou <literal>Property</literal>.
</para>
@ -372,7 +374,7 @@ session.createCriteria(Cat.class)
.list();]]></programlisting>
<para>
Même des requêtes corrélées sont possibles :
Même des requêtes corrélées sont possibles :
</para>
<programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
@ -389,19 +391,19 @@ session.createCriteria(Cat.class, "cat")
could also be explained. -->
<sect1 id="query-criteria-naturalid">
<title>Requêtes par identifiant naturel</title>
<title>Requêtes par identifiant naturel</title>
<para>
Pour la plupart des requêtes, incluant les requêtes par critère, le cache de requêtes
n'est pas très efficace, parce que l'invalidation du cache de requêtes arrive trop
souvent. Cependant, il y a une sorte spéciale de requête où nous pouvons optimiser
Pour la plupart des requêtes, incluant les requêtes par critère, le cache de requêtes
n'est pas très efficace, parce que l'invalidation du cache de requêtes arrive trop
souvent. Cependant, il y a une sorte spéciale de requête où nous pouvons optimiser
l'algorithme d'invalidation du cache : les recherches sur une clef naturelle constante.
Dans certaines applications, cette sorte de requête se produit fréquemment. L'API de
critère fournit une provision spéciale pour ce cas d'utilisation.
Dans certaines applications, cette sorte de requête se produit fréquemment. L'API de
critère fournit une provision spéciale pour ce cas d'utilisation.
</para>
<para>
D'abord vous devriez mapper la clef naturelle de votre entité en utilisant
D'abord vous devriez mapper la clef naturelle de votre entité en utilisant
<literal>&lt;natural-id&gt;</literal>, et activer l'utilisation du cache de second niveau.
</para>
@ -418,12 +420,12 @@ session.createCriteria(Cat.class, "cat")
</class>]]></programlisting>
<para>
Notez que cette fonctionnalité n'est pas prévue pour l'utilisation avec des
entités avec des clefs naturelles <emphasis>mutables</emphasis>.
Notez que cette fonctionnalité n'est pas prévue pour l'utilisation avec des
entités avec des clefs naturelles <emphasis>mutables</emphasis>.
</para>
<para>
Ensuite, activez le cache de requête d'Hibernate.
Ensuite, activez le cache de requête d'Hibernate.
</para>
<para>

View File

@ -1,46 +1,48 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="querysql" revision="2">
<title>SQL natif</title>
<para>
Vous pouvez aussi écrire vos requêtes dans le dialecte SQL natif de votre base de données.
Ceci est utile si vous souhaitez utiliser les fonctionnalités spécifiques de votre base de
données comme le mot clé <literal>CONNECT</literal> d'Oracle. Cette fonctionnalité offre par ailleurs un moyen
de migration plus propre et doux d'une application basée sur SQL/JDBC vers
Vous pouvez aussi écrire vos requêtes dans le dialecte SQL natif de votre base de données.
Ceci est utile si vous souhaitez utiliser les fonctionnalités spécifiques de votre base de
données comme le mot clé <literal>CONNECT</literal> d'Oracle. Cette fonctionnalité offre par ailleurs un moyen
de migration plus propre et doux d'une application basée sur SQL/JDBC vers
une application Hibernate.
</para>
<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>
<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">
<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>,
<para>L'exécution des requêtes en SQL natif est contrôlée par l'interface <literal>SQLQuery</literal>,
laquelle est obtenue en appelant <literal>Session.createSQLQuery()</literal>.
Dans des cas extrêmement simples, nous pouvons utiliser la forme suivante :
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>
<para>Cette requête a spécifié :</para>
<para>Cette requête a spécifié :</para>
<itemizedlist>
<listitem>
<para>la requête SQL</para>
<para>la requête SQL</para>
</listitem>
<listitem>
<para>l'entité retournée par la requête</para>
<para>l'entité retournée par la requête</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
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>
@ -48,26 +50,26 @@
.addEntity("cat", Cat.class)
.list();</programlisting>
<para>Cette requête a spécifié :</para>
<para>Cette requête a spécifié :</para>
<itemizedlist>
<listitem>
<para>la requête SQL, avec un paramètre fictif pour Hibernate pour injecter les alias de colonne</para>
<para>la requête SQL, avec un paramètre fictif pour Hibernate pour injecter les alias de colonne</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, et son alias de table SQL</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.
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>
La méthode <literal>addJoin()</literal> peut être utilisée pour charger des associations vers d'autres
entités et collections.
La méthode <literal>addJoin()</literal> peut être utilisée pour charger des associations vers d'autres
entités et collections.
</para>
<programlisting>List cats = sess.createSQLQuery(
@ -78,15 +80,15 @@
.list();</programlisting>
<para>
Une requête SQL native pourrait retourner une simple valeur scalaire ou une combinaison de scalaires et d'entités.
Une requête SQL native pourrait retourner une simple valeur scalaire ou une combinaison de scalaires et d'entités.
</para>
<programlisting>Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();</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>
<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>
<programlisting>List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
@ -96,20 +98,20 @@
</sect1>
<sect1 id="querysql-aliasreferences">
<title>Alias et références de propriété</title>
<title>Alias et références de propriété</title>
<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.
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>
La syntaxe <literal>{}</literal> <emphasis>n'est pas</emphasis> requise pour le requêtes nommées. Voir
La syntaxe <literal>{}</literal> <emphasis>n'est pas</emphasis> requise pour le requêtes nommées. Voir
<xref linkend="querysql-namedqueries" />.
</para>
@ -124,13 +126,13 @@ List loggedCats = sess.createSQLQuery(sql)
.list();</programlisting>
<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> !
<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>
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.
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>
<table frame="topbot" id="aliasinjection-summary">
@ -155,7 +157,7 @@ List loggedCats = sess.createSQLQuery(sql)
<tbody>
<row>
<entry>Une simple propriété</entry>
<entry>Une simple propriété</entry>
<entry><literal>{[aliasname].[propertyname]}</literal></entry>
@ -163,7 +165,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Une propriété composée</entry>
<entry>Une propriété composée</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
@ -172,7 +174,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Discriminant d'une entité</entry>
<entry>Discriminant d'une entité</entry>
<entry><literal>{[aliasname].class}</literal></entry>
@ -180,7 +182,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Toutes les propriétés d'une entité</entry>
<entry>Toutes les propriétés d'une entité</entry>
<entry><literal>{[aliasname].*}</literal></entry>
@ -204,7 +206,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>L'élément d'une collection</entry>
<entry>L'élément d'une collection</entry>
<entry><literal>{[aliasname].element}</literal></entry>
@ -214,7 +216,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Propriété de l'élément dans la collection</entry>
<entry>Propriété de l'élément dans la collection</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
@ -222,7 +224,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 de l'élément dans la collection</entry>
<entry><literal>{[aliasname].element.*}</literal></entry>
@ -230,7 +232,7 @@ List loggedCats = sess.createSQLQuery(sql)
</row>
<row>
<entry>Toutes les propriétés de la collection</entry>
<entry>Toutes les propriétés de la collection</entry>
<entry><literal>{[aliasname].*}</literal></entry>
@ -242,11 +244,11 @@ List loggedCats = sess.createSQLQuery(sql)
</sect1>
<sect1 id="querysql-namedqueries" revision="3">
<title>Requêtes SQL nommées</title>
<title>Requêtes SQL nommées</title>
<para>
Les requêtes SQL nommées peuvent être définies dans le document de mapping
et appelées exactement de la même manière qu'un requête HQL nommée. Dans ce
Les requêtes SQL nommées peuvent être définies dans le document de mapping
et appelées exactement de la même manière qu'un requête HQL nommée. Dans ce
cas, nous <emphasis>n'avons pas besoin</emphasis> d'appeler <literal>addEntity()</literal>.
</para>
@ -265,9 +267,9 @@ List loggedCats = sess.createSQLQuery(sql)
.list();</programlisting>
<para>
Les éléments <literal>&lt;return-join&gt;</literal> et
<literal>&lt;load-collection&gt;</literal> sont respectivement utilisés pour lier
des associations et définir des requêtes qui initialisent des collections.
Les éléments <literal>&lt;return-join&gt;</literal> et
<literal>&lt;load-collection&gt;</literal> sont respectivement utilisés pour lier
des associations et définir des requêtes qui initialisent des collections.
</para>
<programlisting>&lt;sql-query name="personsWith"&gt;
@ -287,8 +289,8 @@ List loggedCats = sess.createSQLQuery(sql)
&lt;/sql-query&gt;</programlisting>
<para>
Une requête SQL nommée peut retourner une valeur scalaire. Vous devez
spécifier l'alias de colonne et le type Hibernate utilisant l'élément
Une requête SQL nommée peut retourner une valeur scalaire. Vous devez
spécifier l'alias de colonne et le type Hibernate utilisant l'élément
<literal>&lt;return-scalar&gt;</literal> :</para>
<programlisting>&lt;sql-query name="mySqlQuery"&gt;
@ -300,9 +302,9 @@ List loggedCats = sess.createSQLQuery(sql)
&lt;/sql-query&gt;</programlisting>
<para>
Vous pouvez externaliser les informations de mapping des résultats dans un
élément <literal>&lt;resultset&gt;</literal> pour soit les réutiliser
dans différentes requêtes nommées, soit à travers l'API
Vous pouvez externaliser les informations de mapping des résultats dans un
élément <literal>&lt;resultset&gt;</literal> pour soit les réutiliser
dans différentes requêtes nommées, soit à travers l'API
<literal>setResultSetMapping()</literal>.
</para>
@ -326,11 +328,11 @@ List loggedCats = sess.createSQLQuery(sql)
&lt;/sql-query&gt;</programlisting>
<sect2 id="propertyresults">
<title>Utilisation de return-property pour spécifier explicitement les noms des colonnes/alias</title>
<title>Utilisation de return-property pour spécifier explicitement les noms des colonnes/alias</title>
<para>
Avec <literal>&lt;return-property&gt;</literal> vous pouvez explicitement dire
à Hibernate quels alias de colonne utiliser, plutot que d'employer la syntaxe
à Hibernate quels alias de colonne utiliser, plutot que d'employer la syntaxe
<literal>{}</literal> pour laisser Hibernate injecter ses propres alias.
</para>
@ -349,8 +351,8 @@ List loggedCats = sess.createSQLQuery(sql)
<para>
<literal>&lt;return-property&gt;</literal> fonctionne aussi avec de
multiple colonnes. Cela résout une limitation de la syntaxe <literal>{}</literal>
qui ne peut pas permettre une bonne granularité des propriétés multi-colonnes.
multiple colonnes. Cela résout une limitation de la syntaxe <literal>{}</literal>
qui ne peut pas permettre une bonne granularité des propriétés multi-colonnes.
</para>
<programlisting>&lt;sql-query name="organizationCurrentEmployments"&gt;
@ -370,28 +372,28 @@ List loggedCats = sess.createSQLQuery(sql)
&lt;/sql-query&gt;</programlisting>
<para>
Notez que dans cet exemple nous avons utilisé <literal>&lt;return-property&gt;</literal>
Notez que dans cet exemple nous avons utilisé <literal>&lt;return-property&gt;</literal>
en combinaison avec la syntaxe <literal>{}</literal> pour l'injection. Cela autorise les
utilisateurs à choisir comment ils veulent référencer les colonnes et les propriétés.
utilisateurs à choisir comment ils veulent référencer les colonnes et les propriétés.
</para>
<para>
Si votre mapping a un discriminant vous devez utiliser
<literal>&lt;return-discriminator&gt;</literal> pour spécifier la colonne
<literal>&lt;return-discriminator&gt;</literal> pour spécifier la colonne
discriminante.
</para>
</sect2>
<sect2 id="sp_query" revision="1">
<title>Utilisation de procédures stockées pour les requêtes</title>
<title>Utilisation de procédures stockées pour les requêtes</title>
<para>
Hibernate 3 introduit le support des requêtes via procédures stockées et les fonctions.
Hibernate 3 introduit le support des requêtes via procédures stockées et les fonctions.
La documentation suivante est valable pour les deux.
Les procédures stockées/fonctions doivent retourner l'ensemble de résultats en tant que
premier paramètre sortant (NdT: "out-parameter") pour être capable de fonctionner
avec Hibernate. Un exemple d'une telle procédure stockée en Oracle 9 et
version supérieure :
Les procédures stockées/fonctions doivent retourner l'ensemble de résultats en tant que
premier paramètre sortant (NdT: "out-parameter") pour être capable de fonctionner
avec Hibernate. Un exemple d'une telle procédure stockée en Oracle 9 et
version supérieure :
</para>
<programlisting>CREATE OR REPLACE FUNCTION selectAllEmployments
@ -407,7 +409,7 @@ BEGIN
RETURN st_cursor;
END;</programlisting>
<para>Pour utiliser cette requête dans Hibernate vous avez besoin de la mapper via une requête nommée.</para>
<para>Pour utiliser cette requête dans Hibernate vous avez besoin de la mapper via une requête nommée.</para>
<programlisting>&lt;sql-query name="selectAllEmployees_SP" callable="true"&gt;
&lt;return alias="emp" class="Employment"&gt;
@ -426,52 +428,52 @@ BEGIN
&lt;/sql-query&gt;</programlisting>
<para>
Notez que les procédures stockées retournent, pour le moment, seulement des
scalaires et des entités. <literal>&lt;return-join&gt;</literal> et
<literal>&lt;load-collection&gt;</literal> ne sont pas supportés.
Notez que les procédures stockées retournent, pour le moment, seulement des
scalaires et des entités. <literal>&lt;return-join&gt;</literal> et
<literal>&lt;load-collection&gt;</literal> ne sont pas supportés.
</para>
<sect3 id="querysql-limits-storedprocedures" revision="1">
<title>Règles/limitations lors de l'utilisation des procédures stockées</title>
<title>Règles/limitations lors de l'utilisation des procédures stockées</title>
<para>
Pur utiliser des procédures stockées avec Hibernate, les procédures doivent
suivre certaines règles. Si elles ne suivent pas ces règles, elles ne sont pas
utilisables avec Hibernate. Si vous voulez encore utiliser ces procédures vous
devez les exécuter via <literal>session.connection()</literal>. Les règles
sont différentes pour chaque base de données, puisque les vendeurs de base
de données ont des sémantiques/syntaxes différentes pour les procédures stockées.
Pur utiliser des procédures stockées avec Hibernate, les procédures doivent
suivre certaines règles. Si elles ne suivent pas ces règles, elles ne sont pas
utilisables avec Hibernate. Si vous voulez encore utiliser ces procédures vous
devez les exécuter via <literal>session.connection()</literal>. Les règles
sont différentes pour chaque base de données, puisque les vendeurs de base
de données ont des sémantiques/syntaxes différentes pour les procédures stockées.
</para>
<para>Les requêtes de procédures stockées ne peuvent pas être paginées avec
<para>Les requêtes de procédures stockées ne peuvent pas être paginées avec
<literal>setFirstResult()/setMaxResults()</literal>.</para>
<para>Pour Oracle les règles suivantes s'appliquent :</para>
<para>Pour Oracle les règles suivantes s'appliquent :</para>
<itemizedlist spacing="compact">
<listitem>
<para>
La procédure doit retourner un ensemble de résultats. Le
prmeier paramètre d'une procédure doit être un <literal>OUT</literal>
qui retourne un ensemble de résultats. Ceci est fait en
La procédure doit retourner un ensemble de résultats. Le
prmeier paramètre d'une procédure doit être un <literal>OUT</literal>
qui retourne un ensemble de résultats. Ceci est fait en
retournant un <literal>SYS_REFCURSOR</literal> dans Oracle 9 ou 10. Dans
Oracle vous avez besoin de définir un type <literal>REF CURSOR</literal>.</para>
Oracle vous avez besoin de définir un type <literal>REF CURSOR</literal>.</para>
</listitem>
</itemizedlist>
<para>Pour Sybase ou MS SQL server les règles suivantes s'appliquent :</para>
<para>Pour Sybase ou MS SQL server les règles suivantes s'appliquent :</para>
<itemizedlist spacing="compact">
<listitem>
<para>La procédure doit retourner un ensemble de résultats. Notez que comme
ces serveurs peuvent retourner de multiples ensembles de résultats et mettre à jour
des compteurs, Hibernate itérera les résultats et prendra le premier résultat qui est
un ensemble de résultat comme valeur de retour. Tout le reste sera ignoré.</para>
<para>La procédure doit retourner un ensemble de résultats. Notez que comme
ces serveurs peuvent retourner de multiples ensembles de résultats et mettre à jour
des compteurs, Hibernate itérera les résultats et prendra le premier résultat qui est
un ensemble de résultat comme valeur de retour. Tout le reste sera ignoré.</para>
</listitem>
<listitem>
<para>Si vous pouvez activer <literal>SET NOCOUNT ON</literal> dans votre procédure,
<para>Si vous pouvez activer <literal>SET NOCOUNT ON</literal> dans votre procédure,
elle sera probablement plus efficace, mais ce n'est pas une obligation.</para>
</listitem>
</itemizedlist>
@ -480,16 +482,16 @@ BEGIN
</sect1>
<sect1 id="querysql-cud">
<title>SQL personnalisé pour créer, mettre à jour et effacer</title>
<title>SQL personnalisé pour créer, mettre à jour et effacer</title>
<para>
Hibernate3 peut utiliser des expression SQL personnalisées pour des opérations de création,
de mise à jour, et de suppression. Les objets persistants les classes et les collections
dans Hibernate contiennent déjà un ensemble de chaînes de caractères générées lors de la
Hibernate3 peut utiliser des expression SQL personnalisées pour des opérations de création,
de mise à jour, et de suppression. Les objets persistants les classes et les collections
dans Hibernate contiennent déjà un ensemble de chaînes de caractères générées lors de la
configuration (insertsql, deletesql, updatesql, etc). Les tages de mapping
<literal>&lt;sql-insert&gt;</literal>,
<literal>&lt;sql-delete&gt;</literal>, et
<literal>&lt;sql-update&gt;</literal> surchargent ces chaînes de caractères :</para>
<literal>&lt;sql-update&gt;</literal> surchargent ces chaînes de caractères :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
@ -501,11 +503,11 @@ BEGIN
&lt;sql-delete&gt;DELETE FROM PERSON WHERE ID=?&lt;/sql-delete&gt;
&lt;/class&gt;</programlisting>
<para>Le SQL est directement exécuté dans votre base de données, donc vous êtes libre d'utiliser
le dialecte que vous souhaitez. Cela réduira bien sûr la portabilité de votre mapping si vous
utilisez du SQL spécifique à votre base de données.</para>
<para>Le SQL est directement exécuté dans votre base de données, donc vous êtes libre d'utiliser
le dialecte que vous souhaitez. Cela réduira bien sûr la portabilité de votre mapping si vous
utilisez du SQL spécifique à votre base de données.</para>
<para>Les procédures stockées sont supportées si l'attribut <literal>callable</literal> est paramétré :</para>
<para>Les procédures stockées sont supportées si l'attribut <literal>callable</literal> est paramétré :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
@ -517,22 +519,22 @@ BEGIN
&lt;sql-update callable="true"&gt;{? = call updatePerson (?, ?)}&lt;/sql-update&gt;
&lt;/class&gt;</programlisting>
<para>L'ordre des paramètres positionnels est actuellement vital, car ils doivent être dans la
même séquence qu'Hibernate les attend.</para>
<para>L'ordre des paramètres positionnels est actuellement vital, car ils doivent être dans la
même séquence qu'Hibernate les attend.</para>
<para>
Vous pouvez voir l'ordre attendu en activant les journaux de debug pour le
niveau <literal>org.hibernate.persister.entity</literal> level. Avec ce niveau activé,
Hibernate imprimera le SQL statique qui est utilisé pour créer, mettre à jour,
supprimer, etc. des entités. (Pour voir la séquence attendue, rappelez-vous de ne pas
inclure votre SQL personnalisé dans les fichiers de mapping de manière à surcharger le
SQL statique généré par Hibernate.)</para>
niveau <literal>org.hibernate.persister.entity</literal> level. Avec ce niveau activé,
Hibernate imprimera le SQL statique qui est utilisé pour créer, mettre à jour,
supprimer, etc. des entités. (Pour voir la séquence attendue, rappelez-vous de ne pas
inclure votre SQL personnalisé dans les fichiers de mapping de manière à surcharger le
SQL statique généré par Hibernate.)</para>
<para>Les procédures stockées sont dans la plupart des cas (lire : il vaut mieux le faire)
requises pour retourner le nombre de lignes insérées/mises à jour/supprimées, puisque
Hibernate fait quelques vérifications de succès lors de l'exécution de l'expression.
Hibernate inscrit toujours la première expression comme un paramètre de sortie numérique pour les
opérations CUD :</para>
<para>Les procédures stockées sont dans la plupart des cas (lire : il vaut mieux le faire)
requises pour retourner le nombre de lignes insérées/mises à jour/supprimées, puisque
Hibernate fait quelques vérifications de succès lors de l'exécution de l'expression.
Hibernate inscrit toujours la première expression comme un paramètre de sortie numérique pour les
opérations CUD :</para>
<programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
@ -550,9 +552,9 @@ END updatePerson;</programlisting>
</sect1>
<sect1 id="querysql-load">
<title>SQL personnalisé pour le chargement</title>
<title>SQL personnalisé pour le chargement</title>
<para>Vous pouvez aussi déclarer vos propres requêtes SQL (ou HQL) pour le chargement d'entité :</para>
<para>Vous pouvez aussi déclarer vos propres requêtes SQL (ou HQL) pour le chargement d'entité :</para>
<programlisting>&lt;sql-query name="person"&gt;
&lt;return alias="pers" class="Person" lock-mode="upgrade"/&gt;
@ -562,8 +564,8 @@ END updatePerson;</programlisting>
FOR UPDATE
&lt;/sql-query&gt;</programlisting>
<para>Ceci est juste une déclaration de requête nommée, comme vu plus tôt. Vous pouvez référencer
cette requête nommée dans un mapping de classe :</para>
<para>Ceci est juste une déclaration de requête nommée, comme vu plus tôt. Vous pouvez référencer
cette requête nommée dans un mapping de classe :</para>
<programlisting>&lt;class name="Person"&gt;
&lt;id name="id"&gt;
@ -573,9 +575,9 @@ END updatePerson;</programlisting>
&lt;loader query-ref="person"/&gt;
&lt;/class&gt;</programlisting>
<para>Ceci fonctionne même avec des procédures stockées.</para>
<para>Ceci fonctionne même avec des procédures stockées.</para>
<para>Vous pouvez même définir une requête pour le chargement d'une collection :</para>
<para>Vous pouvez même définir une requête pour le chargement d'une collection :</para>
<programlisting>&lt;set name="employments" inverse="true"&gt;
&lt;key/&gt;
@ -591,7 +593,7 @@ END updatePerson;</programlisting>
ORDER BY STARTDATE ASC, EMPLOYEE ASC
&lt;/sql-query&gt;</programlisting>
<para>Vous pourriez même définir un chargeur d'entité qui charge une collection par jointure :</para>
<para>Vous pourriez même définir un chargeur d'entité qui charge une collection par jointure :</para>
<programlisting>&lt;sql-query name="person"&gt;
&lt;return alias="pers" class="Person"/&gt;

View File

@ -1,85 +1,87 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="toolsetguide" revision="2">
<title>Guide des outils</title>
<para>
Des outils en ligne de commande, des plugins Eclipse ainsu que des tâches Ant permettent de gérer de cycles de développement complet
Des outils en ligne de commande, des plugins Eclipse ainsu que des tâches Ant permettent de gérer de cycles de développement complet
de projets utilisant Hibernate.
</para>
<para>
Les <emphasis>outils Hibernate</emphasis> actuels incluent des plugins pour l'IDE Eclipse ainsi que des tâches Ant pour l'ingénierie
inverse de bases de données existantes :
Les <emphasis>outils Hibernate</emphasis> actuels incluent des plugins pour l'IDE Eclipse ainsi que des tâches Ant pour l'ingénierie
inverse de bases de données existantes :
</para>
<itemizedlist>
<listitem><para>
<emphasis>Mapping Editor :</emphasis> un éditeur pour les fichiers de mapping XML Hibernate, supportant l'auto-complétion et la mise en valeur de la syntaxe.
Il supporte aussi l'auto-complétion automatique pour les noms de classes et les noms de propriété/champ,
le rendant beaucoup plus polyvalent qu'un éditeurXML normal.
<emphasis>Mapping Editor :</emphasis> un éditeur pour les fichiers de mapping XML Hibernate, supportant l'auto-complétion et la mise en valeur de la syntaxe.
Il supporte aussi l'auto-complétion automatique pour les noms de classes et les noms de propriété/champ,
le rendant beaucoup plus polyvalent qu'un éditeurXML normal.
</para></listitem>
<listitem><para>
<emphasis>Console :</emphasis> la console est une nouvelle vue d'Eclipse. En plus de la vue d'ensemble
arborescente de vos configurations de console, vous obtenez aussi une vue interactive de vos classes persistantes et de leurs relations.
La console vous permet d'exécuter des requête HQL dans votre base de données et de parcourir les résultats directement dans Eclipse.
La console vous permet d'exécuter des requête HQL dans votre base de données et de parcourir les résultats directement dans Eclipse.
</para></listitem>
<listitem><para>
<emphasis>Development Wizards :</emphasis> plusieurs assistants sont fournis avec les outils d'Hibernate
pour Eclipse ; vous pouvez utiliser un assistant pour générer rapidement les fichiers de configuration d'Hibernate (cfg.xml),
ou vous pouvez même complètement générer les fichiers de mapping Hibernate et les sources des POJOs à partir d'un schéma de base de données existant.
L'assistant d'ingénierie inverse supporte les modèles utilisateur.
pour Eclipse ; vous pouvez utiliser un assistant pour générer rapidement les fichiers de configuration d'Hibernate (cfg.xml),
ou vous pouvez même complètement générer les fichiers de mapping Hibernate et les sources des POJOs à partir d'un schéma de base de données existant.
L'assistant d'ingénierie inverse supporte les modèles utilisateur.
</para></listitem>
<listitem><para>
<emphasis>Tâches Ant :</emphasis>
<emphasis>Tâches Ant :</emphasis>
</para></listitem>
</itemizedlist>
<para>
Veuillez-vous référer au paquet <emphasis>outils Hibernate</emphasis> et sa documentation pour plus d'informations.
Veuillez-vous référer au paquet <emphasis>outils Hibernate</emphasis> et sa documentation pour plus d'informations.
</para>
<para>
Pourtant, le paquet principal d'Hibernate arrive avec un lot d'outils intégrés (il peut même être utilisé de "l'intérieur" d'Hibernate à la volée) :
Pourtant, le paquet principal d'Hibernate arrive avec un lot d'outils intégrés (il peut même être utilisé de "l'intérieur" d'Hibernate à la volée) :
<emphasis>SchemaExport</emphasis> aussi connu comme
<literal>hbm2ddl</literal>.
</para>
<sect1 id="toolsetguide-s1" revision="2">
<title>Génération automatique du schéma</title>
<title>Génération automatique du schéma</title>
<para>
La DDL peut être générée à partir de vos fichiers de mapping par un utilitaire d'Hibernate. Le schéma généré
inclut les contraintes d'intégrité référentielle (clefs primaires et étrangères) pour les tables d'entités
et de collections. Les tables et les séquences sont aussi créées pour les générateurs d'identifiant mappés.
La DDL peut être générée à partir de vos fichiers de mapping par un utilitaire d'Hibernate. Le schéma généré
inclut les contraintes d'intégrité référentielle (clefs primaires et étrangères) pour les tables d'entités
et de collections. Les tables et les séquences sont aussi créées pour les générateurs d'identifiant mappés.
</para>
<para>
Vous <emphasis>devez</emphasis> spécifier un <literal>Dialect</literal> SQL via la propriété
Vous <emphasis>devez</emphasis> spécifier un <literal>Dialect</literal> SQL via la propriété
<literal>hibernate.dialect</literal> lors de l'utilisation de cet outils, puisque la DDL est
fortement dépendante de la base de données.
fortement dépendante de la base de données.
</para>
<para>
D'abord, personnalisez vos fichiers de mapping pour améliorer le schéma généré.
D'abord, personnalisez vos fichiers de mapping pour améliorer le schéma généré.
</para>
<sect2 id="toolsetguide-s1-2" revision="3">
<title>Personnaliser le schéma</title>
<title>Personnaliser le schéma</title>
<para>
Plusieurs éléments du mapping hibernate définissent des attributs optionnels
nommés <literal>length</literal>, <literal>precision</literal> et <literal>scale</literal>.
Vous pouvez paramétrer la longueur, la précision,... d'une colonne avec ces attributs.
Plusieurs éléments du mapping hibernate définissent des attributs optionnels
nommés <literal>length</literal>, <literal>precision</literal> et <literal>scale</literal>.
Vous pouvez paramétrer la longueur, la précision,... d'une colonne avec ces attributs.
</para>
<programlisting><![CDATA[<property name="zip" length="5"/>]]></programlisting>
<programlisting><![CDATA[<property name="balance" precision="12" scale="2"/>]]></programlisting>
<para>
Certains éléments acceptent aussi un attribut <literal>not-null</literal>
(utilisé pour générer les contraintes de colonnes <literal>NOT NULL</literal>) et
un attribut <literal>unique</literal> (pour générer une contrainte de colonne
Certains éléments acceptent aussi un attribut <literal>not-null</literal>
(utilisé pour générer les contraintes de colonnes <literal>NOT NULL</literal>) et
un attribut <literal>unique</literal> (pour générer une contrainte de colonne
<literal>UNIQUE</literal>).
</para>
@ -87,10 +89,10 @@
<programlisting><![CDATA[<element column="serialNumber" type="long" not-null="true" unique="true"/>]]></programlisting>
<para>
Un attribut <literal>unique-key</literal> peut être utilisé pour grouper les colonnes
en une seule contrainte d'unicité. Actuellement, la valeur spécifiée par
l'attribut <literal>unique-key</literal> n'est <emphasis>pas</emphasis> utilisée pour
nommer la contrainte dans le DDL généré, elle sert juste à grouper les colonnes
Un attribut <literal>unique-key</literal> peut être utilisé pour grouper les colonnes
en une seule contrainte d'unicité. Actuellement, la valeur spécifiée par
l'attribut <literal>unique-key</literal> n'est <emphasis>pas</emphasis> utilisée pour
nommer la contrainte dans le DDL généré, elle sert juste à grouper les colonnes
dans le fichier de mapping.
</para>
<programlisting><![CDATA[<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
@ -98,8 +100,8 @@
<para>
Un attribut <literal>index</literal> indique le nom d'un index qui sera
créé en utilisant la ou les colonnes mappées. Plusieurs colonnes
peuvent être groupées dans un même index, en spécifiant le même
créé en utilisant la ou les colonnes mappées. Plusieurs colonnes
peuvent être groupées dans un même index, en spécifiant le même
nom d'index.
</para>
@ -107,14 +109,14 @@
<property name="firstName" index="CustName"/>]]></programlisting>
<para>
Un attribut <literal>foreign-key</literal> peut être utilisé pour surcharger le nom
des clés étrangères générées.
Un attribut <literal>foreign-key</literal> peut être utilisé pour surcharger le nom
des clés étrangères générées.
</para>
<programlisting><![CDATA[<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>]]></programlisting>
<para>
Plusieurs éléments de mapping acceptent aussi un élément fils <literal>&lt;column&gt;</literal>.
Plusieurs éléments de mapping acceptent aussi un élément fils <literal>&lt;column&gt;</literal>.
Ceci est utile pour les type multi-colonnes:
</para>
@ -125,9 +127,9 @@
</property>]]></programlisting>
<para>
L'attribut <literal>default</literal> vous laisse spécifier une valeur par défaut pour
une colonnes (vous devriez assigner la même valeur à la propriété mappée avant de sauvegarder une nouvelle instance
de la classe mappée).
L'attribut <literal>default</literal> vous laisse spécifier une valeur par défaut pour
une colonnes (vous devriez assigner la même valeur à la propriété mappée avant de sauvegarder une nouvelle instance
de la classe mappée).
</para>
<programlisting><![CDATA[<property name="credits" type="integer" insert="false">
@ -140,7 +142,7 @@
<para>
L'attribut <literal>sql-type</literal> laisse l'utilisateur surcharger le mapping
par défaut du type Hibernate vers un type SQL.
par défaut du type Hibernate vers un type SQL.
</para>
<programlisting><![CDATA[<property name="balance" type="float">
@ -149,7 +151,7 @@
<para>
L'attribut <literal>check</literal> permet de spécifier une contrainte de vérification.
L'attribut <literal>check</literal> permet de spécifier une contrainte de vérification.
</para>
<programlisting><![CDATA[<property name="foo" type="integer">
@ -172,53 +174,53 @@
<row>
<entry>Attribut</entry>
<entry>Valeur</entry>
<entry>Interprétation</entry>
<entry>Interprétation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>length</literal></entry>
<entry>numérique</entry>
<entry>numérique</entry>
<entry>taille d'une colonne</entry>
</row>
<row>
<entry><literal>precision</literal></entry>
<entry>numérique</entry>
<entry>précision décimale de la colonne</entry>
<entry>numérique</entry>
<entry>précision décimale de la colonne</entry>
</row>
<row>
<entry><literal>scale</literal></entry>
<entry>numérique</entry>
<entry>scale décimale de la colonne</entry>
<entry>numérique</entry>
<entry>scale décimale de la colonne</entry>
</row>
<row>
<entry><literal>not-null</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>spécifie que la colonne doit être non-nulle</entry>
<entry>spécifie que la colonne doit être non-nulle</entry>
</row>
<row>
<entry><literal>unique</literal></entry>
<entry><literal>true|false</literal></entry>
<entry>spécifie que la colonne doit avoir une contrainte d'unicité</entry>
<entry>spécifie que la colonne doit avoir une contrainte d'unicité</entry>
</row>
<row>
<entry><literal>index</literal></entry>
<entry><literal>index_name</literal></entry>
<entry>spécifie le nom d'un index (multi-colonnes)</entry>
<entry>spécifie le nom d'un index (multi-colonnes)</entry>
</row>
<row>
<entry><literal>unique-key</literal></entry>
<entry><literal>unique_key_name</literal></entry>
<entry>spécifie le nom d'une contrainte d'unicité multi-colonnes</entry>
<entry>spécifie le nom d'une contrainte d'unicité multi-colonnes</entry>
</row>
<row>
<entry><literal>foreign-key</literal></entry>
<entry><literal>foreign_key_name</literal></entry>
<entry>
spécifie le nom d'une contrainte de clé étrangère générée pour
une association, utilisez-la avec les éléments de mapping
spécifie le nom d'une contrainte de clé étrangère générée pour
une association, utilisez-la avec les éléments de mapping
&lt;one-to-one&gt;, &lt;many-to-one&gt;, &lt;key&gt;, et &lt;many-to-many&gt;
Notez que les extrêmités <literal>inverse="true"</literal>
Notez que les extrêmités <literal>inverse="true"</literal>
se seront pas prises en compte par <literal>SchemaExport</literal>.
</entry>
</row>
@ -226,22 +228,22 @@
<entry><literal>sql-type</literal></entry>
<entry><literal>SQL column_type</literal></entry>
<entry>
surcharge le type par défaut (attribut de
l'élément <literal>&lt;column&gt;</literal> uniquement)
surcharge le type par défaut (attribut de
l'élément <literal>&lt;column&gt;</literal> uniquement)
</entry>
</row>
<row>
<entry><literal>default</literal></entry>
<entry>expression SQL</entry>
<entry>
spécifie une valeur par défaut pour la colonne
spécifie une valeur par défaut pour la colonne
</entry>
</row>
<row>
<entry><literal>check</literal></entry>
<entry>SQL expression</entry>
<entry>
crée une contrainte de vérification sur la table ou la colonne
crée une contrainte de vérification sur la table ou la colonne
</entry>
</row>
</tbody>
@ -249,7 +251,7 @@
</table>
<para>
L'élément <literal>&lt;comment&gt;</literal> vous permet de spécifier un commentaire pour le schéma généré.
L'élément <literal>&lt;comment&gt;</literal> vous permet de spécifier un commentaire pour le schéma généré.
</para>
<programlisting><![CDATA[<class name="Customer" table="CurCust">
@ -264,19 +266,19 @@
</property>]]></programlisting>
<para>
Ceci a pour résultat une expression
Ceci a pour résultat une expression
<literal>comment on table</literal> ou
<literal>comment on column</literal> dans la DDL générée (où supportée).
<literal>comment on column</literal> dans la DDL générée (où supportée).
</para>
</sect2>
<sect2 id="toolsetguide-s1-3" revision="2">
<title>Exécuter l'outil</title>
<title>Exécuter l'outil</title>
<para>
L'outil <literal>SchemaExport</literal> génère un script DDL vers
la sortie standard et/ou exécute les ordres DDL.
L'outil <literal>SchemaExport</literal> génère un script DDL vers
la sortie standard et/ou exécute les ordres DDL.
</para>
<para>
@ -298,7 +300,7 @@
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>ne pas écrire le script vers la sortie standard</entry>
<entry>ne pas écrire le script vers la sortie standard</entry>
</row>
<row>
<entry><literal>--drop</literal></entry>
@ -306,42 +308,42 @@
</row>
<row>
<entry><literal>--create</literal></entry>
<entry>ne créé que les tables</entry>
<entry>ne créé que les tables</entry>
</row>
<row>
<entry><literal>--text</literal></entry>
<entry>ne pas exécuter sur la base de données</entry>
<entry>ne pas exécuter sur la base de données</entry>
</row>
<row>
<entry><literal>--output=my_schema.ddl</literal></entry>
<entry>écrit le script ddl vers un fichier</entry>
<entry>écrit le script ddl vers un fichier</entry>
</row>
<row>
<entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
<entry>sélectionne une <literal>NamingStrategy</literal></entry>
<entry>sélectionne une <literal>NamingStrategy</literal></entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
<entry>lit la configuration Hibernate à partir d'un fichier XML</entry>
<entry>lit la configuration Hibernate à partir d'un fichier XML</entry>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>lit les propriétés de la base de données à partir d'un fichier</entry>
<entry>lit les propriétés de la base de données à partir d'un fichier</entry>
</row>
<row>
<entry><literal>--format</literal></entry>
<entry>formatte proprement le SQL généré dans le script</entry>
<entry>formatte proprement le SQL généré dans le script</entry>
</row>
<row>
<entry><literal>--delimiter=x</literal></entry>
<entry>paramètre un délimiteur de fin de ligne pour le script</entry>
<entry>paramètre un délimiteur de fin de ligne pour le script</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Vous pouvez même intégrer <literal>SchemaExport</literal> dans votre application :
Vous pouvez même intégrer <literal>SchemaExport</literal> dans votre application :
</para>
<programlisting><![CDATA[Configuration cfg = ....;
@ -350,26 +352,26 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-4">
<title>Propriétés</title>
<title>Propriétés</title>
<para>
Les propriétés de la base de données peuvent être spécifiées
Les propriétés de la base de données peuvent être spécifiées
</para>
<itemizedlist spacing="compact">
<listitem>
<para>comme propriétés système avec <literal>-D</literal><emphasis>&lt;property&gt;</emphasis></para>
<para>comme propriétés système avec <literal>-D</literal><emphasis>&lt;property&gt;</emphasis></para>
</listitem>
<listitem>
<para>dans <literal>hibernate.properties</literal></para>
</listitem>
<listitem>
<para>dans un fichier de propriétés déclaré avec <literal>--properties</literal></para>
<para>dans un fichier de propriétés déclaré avec <literal>--properties</literal></para>
</listitem>
</itemizedlist>
<para>
Les propriétés nécessaires sont :
Les propriétés nécessaires sont :
</para>
<table frame="topbot">
@ -379,7 +381,7 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
<colspec colwidth="2*"/>
<thead>
<row>
<entry>Nom de la propriété</entry>
<entry>Nom de la propriété</entry>
<entry>Description</entry>
</row>
</thead>
@ -394,7 +396,7 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
</row>
<row>
<entry><literal>hibernate.connection.username</literal></entry>
<entry>utilisateur de la base de données</entry>
<entry>utilisateur de la base de données</entry>
</row>
<row>
<entry><literal>hibernate.connection.password</literal></entry>
@ -439,12 +441,12 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-6" revision="2">
<title>Mises à jour incrémentales du schéma</title>
<title>Mises à jour incrémentales du schéma</title>
<para>
L'outil <literal>SchemaUpdate</literal> mettra à jour un schéma existant
en effectuant les changement par "incrément".
Notez que <literal>SchemaUpdate</literal> dépends beaucoup de l'API JDBC
L'outil <literal>SchemaUpdate</literal> mettra à jour un schéma existant
en effectuant les changement par "incrément".
Notez que <literal>SchemaUpdate</literal> dépends beaucoup de l'API JDBC
metadata, il ne fonctionnera donc pas avec tous les drivers JDBC.
</para>
@ -467,11 +469,11 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
<tbody>
<row>
<entry><literal>--quiet</literal></entry>
<entry>ne pas écrire vers la sortie standard</entry>
<entry>ne pas écrire vers la sortie standard</entry>
</row>
<row>
<entry><literal>--text</literal></entry>
<entry>ne pas exporter vers la base de données</entry>
<entry>ne pas exporter vers la base de données</entry>
</row>
<row>
<entry><literal>--naming=eg.MyNamingStrategy</literal></entry>
@ -479,14 +481,14 @@ new SchemaExport(cfg).create(false, true);]]></programlisting>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>lire les propriétés de la base de données à partir d'un fichier</entry>
<entry>lire les propriétés de la base de données à partir d'un fichier</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Vous pouvez intégrer <literal>SchemaUpdate</literal> dans votre application :
Vous pouvez intégrer <literal>SchemaUpdate</literal> dans votre application :
</para>
<programlisting><![CDATA[Configuration cfg = ....;
@ -495,7 +497,7 @@ new SchemaUpdate(cfg).execute(false);]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-7">
<title>Utiliser Ant pour des mises à jour de schéma par incrément</title>
<title>Utiliser Ant pour des mises à jour de schéma par incrément</title>
<para>
Vous pouvez appeler <literal>SchemaUpdate</literal> depuis le script Ant :
@ -516,12 +518,12 @@ new SchemaUpdate(cfg).execute(false);]]></programlisting>
</target>]]></programlisting>
<sect2 id="toolsetguide-s1-8" revision="1">
<title>Validation du schéma</title>
<title>Validation du schéma</title>
<para>
L'outil <literal>SchemaValidator</literal> validera que le schéma existant correspond à vos documents de mapping.
Notez que le <literal>SchemaValidator</literal> dépends de l'API metadata de JDBC, il ne fonctionnera
donc pas avec tous les drivers JDBC. Cet outil est extrêmement utile pour tester.
L'outil <literal>SchemaValidator</literal> validera que le schéma existant correspond à vos documents de mapping.
Notez que le <literal>SchemaValidator</literal> dépends de l'API metadata de JDBC, il ne fonctionnera
donc pas avec tous les drivers JDBC. Cet outil est extrêmement utile pour tester.
</para>
<para>
@ -547,7 +549,7 @@ new SchemaUpdate(cfg).execute(false);]]></programlisting>
</row>
<row>
<entry><literal>--properties=hibernate.properties</literal></entry>
<entry>lit les propriétés dela base de données depuis un fichier de propriétés</entry>
<entry>lit les propriétés dela base de données depuis un fichier de propriétés</entry>
</row>
<row>
<entry><literal>--config=hibernate.cfg.xml</literal></entry>
@ -568,7 +570,7 @@ new SchemaValidator(cfg).validate();]]></programlisting>
</sect2>
<sect2 id="toolsetguide-s1-9">
<title>Utiliser Ant pour la validation du Schéma</title>
<title>Utiliser Ant pour la validation du Schéma</title>
<para>
Vous pouvez appeler <literal>SchemaValidator</literal> depuis le script Ant:

View File

@ -1,50 +1,52 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="xml">
<title>Mapping XML</title>
<para><emphasis>
Notez que cette fonctionnalité est expérimentale dans Hibernate 3.0 et
est en développement extrêmement actif.
Notez que cette fonctionnalité est expérimentale dans Hibernate 3.0 et
est en développement extrêmement actif.
</emphasis></para>
<sect1 id="xml-intro" revision="1">
<title>Travailler avec des données XML</title>
<title>Travailler avec des données XML</title>
<para>
Hibernate vous laisse travailler avec des données XML persistantes de la
même manière que vous travaillez avec des POJOs persistants. Un arbre XML
peut être vu comme une autre manière de représenter les données relationnelles
au niveau objet, à la place des POJOs.
Hibernate vous laisse travailler avec des données XML persistantes de la
même manière que vous travaillez avec des POJOs persistants. Un arbre XML
peut être vu comme une autre manière de représenter les données relationnelles
au niveau objet, à la place des POJOs.
</para>
<para>
Hibernate supporte dom4j en tant qu'API pour la manipulation des arbres XML.
Vous pouvez écrire des requêtes qui récupèrent des arbres dom4j à partie de la
base de données, et avoir toutes les modifications que vous faites sur l'arbre
automatiquement synchronisées dans la base de données. Vous pouvez même prendre
un document XML, l'analyser en utilisant dom4j, et l'écrire dans la base de
données via les opérations basiques d'Hibernate :
Vous pouvez écrire des requêtes qui récupèrent des arbres dom4j à partie de la
base de données, et avoir toutes les modifications que vous faites sur l'arbre
automatiquement synchronisées dans la base de données. Vous pouvez même prendre
un document XML, l'analyser en utilisant dom4j, et l'écrire dans la base de
données via les opérations basiques d'Hibernate :
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
(merge() n'est pas encore supporté).
(merge() n'est pas encore supporté).
</para>
<para>
Cette fonctionnalité a plusieurs applications dont l'import/export de données,
l'externalisation d'entités via JMS ou SOAP et les rapports XSLT.
Cette fonctionnalité a plusieurs applications dont l'import/export de données,
l'externalisation d'entités via JMS ou SOAP et les rapports XSLT.
</para>
<para>
Un simple mapping peut être utilisé pour simultanément mapper les propriétés
d'une classe et les noeuds d'un document XML vers la base de données, ou,
si il n'y a pas de classe à mapper, il peut être utilisé juste pour mapper
Un simple mapping peut être utilisé pour simultanément mapper les propriétés
d'une classe et les noeuds d'un document XML vers la base de données, ou,
si il n'y a pas de classe à mapper, il peut être utilisé juste pour mapper
le XML.
</para>
<sect2 id="xml-intro-mapping">
<title>Spécifier le mapping XML et le mapping d'une classe ensemble</title>
<title>Spécifier le mapping XML et le mapping d'une classe ensemble</title>
<para>
Voici un exemple de mapping d'un POJO et du XML simultanément :
Voici un exemple de mapping d'un POJO et du XML simultanément :
</para>
<programlisting><![CDATA[<class name="Account"
@ -70,7 +72,7 @@
</sect2>
<sect2 id="xml-onlyxml">
<title>Spécifier seulement un mapping XML</title>
<title>Spécifier seulement un mapping XML</title>
<para>
Voici un exemple dans lequel il n'y a pas de class POJO :
@ -101,10 +103,10 @@
</class>]]></programlisting>
<para>
Ce mapping vous permet d'accéder aux données comme un arbre dom4j, ou comme
un graphe de paire nom de propriété/valeur (<literal>Map</literal>s java). Les
noms des propriétés sont des constructions purement logiques qui peuvent être
référées des dans requêtes HQL.
Ce mapping vous permet d'accéder aux données comme un arbre dom4j, ou comme
un graphe de paire nom de propriété/valeur (<literal>Map</literal>s java). Les
noms des propriétés sont des constructions purement logiques qui peuvent être
référées des dans requêtes HQL.
</para>
</sect2>
@ -112,45 +114,45 @@
</sect1>
<sect1 id="xml-mapping" revision="1">
<title>Métadonnées du mapping XML</title>
<title>Métadonnées du mapping XML</title>
<para>
Plusieurs éléments du mapping Hibernate acceptent l'attribut <literal>node</literal>.
Ceci vous permet de spécifier le nom d'un attribut XML ou d'un élément qui
contient la propriété ou les données de l'entité. Le format de l'attribut
<literal>node</literal> doit être un des suivants :
Plusieurs éléments du mapping Hibernate acceptent l'attribut <literal>node</literal>.
Ceci vous permet de spécifier le nom d'un attribut XML ou d'un élément qui
contient la propriété ou les données de l'entité. Le format de l'attribut
<literal>node</literal> doit être un des suivants :
</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>"element-name"</literal> - mappe vers l'élément XML nommé</para>
<para><literal>"element-name"</literal> - mappe vers l'élément XML nommé</para>
</listitem>
<listitem>
<para><literal>"@attribute-name"</literal> - mappe vers l'attribut XML nommé</para>
<para><literal>"@attribute-name"</literal> - mappe vers l'attribut XML nommé</para>
</listitem>
<listitem>
<para><literal>"."</literal> - mappe vers le parent de l'élément</para>
<para><literal>"."</literal> - mappe vers le parent de l'élément</para>
</listitem>
<listitem>
<para>
<literal>"element-name/@attribute-name"</literal> -
mappe vers l'élément nommé de l'attribut nommé
mappe vers l'élément nommé de l'attribut nommé
</para>
</listitem>
</itemizedlist>
<para>
Pour des collections et de simples associations valuées, il y a un attribut
<literal>embed-xml</literal> supplémentaire. Si <literal>embed-xml="true"</literal>,
qui est la valeur par défaut, l'arbre XML pour l'entité associée (ou la collection
des types de valeurs) sera embarquée directement dans l'arbre XML pour l'entité qui
possède l'association. Sinon, si <literal>embed-xml="false"</literal>, alors
seule la valeur de l'identifiant référencé apparaîtra dans le XML pour de simples
associations de points, et les collections n'appraîtront simplement pas.
Pour des collections et de simples associations valuées, il y a un attribut
<literal>embed-xml</literal> supplémentaire. Si <literal>embed-xml="true"</literal>,
qui est la valeur par défaut, l'arbre XML pour l'entité associée (ou la collection
des types de valeurs) sera embarquée directement dans l'arbre XML pour l'entité qui
possède l'association. Sinon, si <literal>embed-xml="false"</literal>, alors
seule la valeur de l'identifiant référencé apparaîtra dans le XML pour de simples
associations de points, et les collections n'appraîtront simplement pas.
</para>
<para>
Vous devriez faire attention à ne pas laisser <literal>embed-xml="true"</literal>
Vous devriez faire attention à ne pas laisser <literal>embed-xml="true"</literal>
pour trop d'associations, puisque XML ne traite pas bien les liens circurlaires.
</para>
@ -190,14 +192,14 @@
</class>]]></programlisting>
<para>
dans ce cas, nous avons décidé d'embarquer la collection d'identifiants de compte,
mais pas les données actuelles du compte. La requête HQL suivante :
dans ce cas, nous avons décidé d'embarquer la collection d'identifiants de compte,
mais pas les données actuelles du compte. La requête HQL suivante :
</para>
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
<para>
devrait retourner l'ensemble de données suivant :
devrait retourner l'ensemble de données suivant :
</para>
<programlisting><![CDATA[<customer id="123456789">
@ -213,8 +215,8 @@
<para>
Si vous positionnez <literal>embed-xml="true"</literal> sur le mapping
<literal>&lt;one-to-many&gt;</literal>, les données pourraient
ressembler plus à ça :
<literal>&lt;one-to-many&gt;</literal>, les données pourraient
ressembler plus à ça :
</para>
<programlisting><![CDATA[<customer id="123456789">
@ -238,11 +240,11 @@
<sect1 id="xml-manipulation" revision="1">
<title>Manipuler des données XML</title>
<title>Manipuler des données XML</title>
<para>
Relisons et mettons à jour des documents XML dans l'application. Nous faisons
ça en obtenant une session dom4j :
Relisons et mettons à jour des documents XML dans l'application. Nous faisons
ça en obtenant une session dom4j :
</para>
<programlisting><![CDATA[Document doc = ....;
@ -281,9 +283,9 @@ tx.commit();
session.close();]]></programlisting>
<para>
Il est extrêmement utile de combiner cette fonctionnalité avec l'opération
<literal>replicate()</literal> d'Hibernate pour implémenter des imports/exports
de données XML.
Il est extrêmement utile de combiner cette fonctionnalité avec l'opération
<literal>replicate()</literal> d'Hibernate pour implémenter des imports/exports
de données XML.
</para>
</sect1>

View File

@ -1,4 +1,4 @@
<?xml version='1.0'?>
<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE legalnotice PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!--

View File

@ -1,34 +0,0 @@
<?xml version='1.0'?>
<!DOCTYPE authorgroup PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<authorgroup id="AuthorGroup">
<othercredit class="translator">
<firstname>Vincent</firstname>
<surname>Ricard</surname>
</othercredit>
<othercredit class="translator">
<firstname>Sebastien</firstname>
<surname>Cesbron</surname>
</othercredit>
<othercredit class="translator">
<firstname>Michael</firstname>
<surname>Courcy</surname>
</othercredit>
<othercredit class="translator">
<firstname>Vincent</firstname>
<surname>Giguère</surname>
</othercredit>
<othercredit class="translator">
<firstname>Baptiste</firstname>
<surname>Mathus</surname>
</othercredit>
<othercredit class="translator">
<firstname>Emmanuel</firstname>
<surname>Bernard</surname>
</othercredit>
<othercredit class="translator">
<firstname>Anthony</firstname>
<surname>Patricio</surname>
</othercredit>
</authorgroup>