hibernate-orm/reference/fr/modules/collection_mapping.xml
Anthony Patricio f3ad07df1b Added: french translation
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@8684 1b8cb986-b30d-0410-93ca-fae66ebed9b2
2005-11-27 16:17:00 +00:00

1217 lines
51 KiB
XML

<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="collections">
<title>Mapping des collections</title>
<sect1 id="collections-persistent" revision="3">
<title>Collections persistantes</title>
<para>
Hibernate requiert que les champs contenant des collections persistantes soient déclarés
comme des types d'interface, par exemple :
</para>
<programlisting><![CDATA[public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}]]></programlisting>
<para>
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>org.hibernate.usertype.UserCollectionType</literal>.)
</para>
<para>
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
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>.
Prenez garde aux erreurs :
</para>
<programlisting><![CDATA[Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
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
<literal>HashMap</literal>, <literal>HashSet</literal>,
<literal>TreeMap</literal>, <literal>TreeSet</literal> ou
<literal>ArrayList</literal>, selon le type de l'interface.
</para>
<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.
</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).
</para>
</sect1>
<sect1 id="collections-mapping" revision="4">
<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>.
</para>
<programlisting><![CDATA[<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class>]]></programlisting>
<para>
À 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 :
</para>
<programlistingco>
<areaspec>
<area id="mappingcollection1" coords="2 65"/>
<area id="mappingcollection2" coords="3 65"/>
<area id="mappingcollection3" coords="4 65"/>
<area id="mappingcollection4" coords="5 65"/>
<area id="mappingcollection5" coords="6 65"/>
<area id="mappingcollection6" coords="7 65"/>
<area id="mappingcollection7" coords="8 65"/>
<area id="mappingcollection8" coords="9 65"/>
<area id="mappingcollection9" coords="10 65"/>
<area id="mappingcollection10" coords="11 65"/>
<area id="mappingcollection11" coords="12 65"/>
<area id="mappingcollection12" coords="13 65"/>
<area id="mappingcollection13" coords="14 65"/>
<area id="mappingcollection14" coords="15 65"/>
</areaspec>
<programlisting><![CDATA[<map
name="nomDePropriete"
table="nom_de_table"
schema="nom_du_schema"
lazy="true|extra|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan"
sort="unsorted|natural|ClasseDeComparateur"
order-by="nom_de_column asc|desc"
where="condition sql where quelcconque"
fetch="join|select|subselect"
batch-size="N"
access="field|property|NomDeClasse"
optimistic-lock="true|false"
mutable="true|false"
node="nom-d-element|."
embed-xml="true|false"
>
<key .... />
<map-key .... />
<element .... />
</map>]]></programlisting>
<calloutlist>
<callout arearefs="mappingcollection1">
<para>
<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)
</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
</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
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
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
</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)
</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>
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)
</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
</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
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é
</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)
</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
collection ne changent jamais (une optimisation mineure dans certains cas)
</para>
</callout>
</calloutlist>
</programlistingco>
<sect2 id="collections-foreignkeys" >
<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)
<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>.
</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
<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>.
</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>.
</para>
</sect2>
<sect2 id="collections-elements" >
<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.
</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
<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é.
</para>
</sect2>
<sect2 id="collections-indexed">
<title>Collections indexées</title>
<para>
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>&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).
</para>
<programlistingco>
<areaspec>
<area id="index1" coords="2 45"/>
<area id="index2" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<list-index
column="nom_de_colonne"
base="0|1|..."/>]]></programlisting>
<calloutlist>
<callout arearefs="index1">
<para>
<literal>nom_de_colonne</literal> (requis) : le nom de la colonne contenant les valeurs de l'index de la collection
</para>
</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
</para>
</callout>
</calloutlist>
</programlistingco>
<programlistingco>
<areaspec>
<area id="mapkey1" coords="2 45"/>
<area id="mapkey2" coords="3 45"/>
<area id="mapkey3" coords="4 45"/>
</areaspec>
<programlisting><![CDATA[<map-key
column="nom_de_colonne"
formula="n'importe quelle expression SQL"
type="nom_du_type"
node="@nom-d-attribut"
length="N"/>]]></programlisting>
<calloutlist>
<callout arearefs="mapkey1">
<para>
<literal>column</literal> (optionnel) :
le nom de la colonne contenant les valeurs de l'index de la collection
</para>
</callout>
<callout arearefs="mapkey2">
<para>
<literal>formula</literal> (optionnel) :
une formule SQL utilisée pour évaluer la clef de la map
</para>
</callout>
<callout arearefs="mapkey3">
<para>
<literal>type</literal> (reguis): le type des clefs de la map
</para>
</callout>
</calloutlist>
</programlistingco>
<programlistingco>
<areaspec>
<area id="indexmanytomany1" coords="2 45"/>
<area id="indexmanytomany2" coords="3 45"/>
<area id="indexmanytomany3" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<map-key-many-to-many
column="nom_de_colonne"
formula="n'importe quelle expression SQL"
class="NomDeClasse"
/>]]></programlisting>
<calloutlist>
<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
</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
</para>
</callout>
<callout arearefs="indexmanytomany3">
<para>
<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
<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é.
</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.
</para>
<sect2 id="collections-ofvalues" revision="2">
<title>Collections de valeurs et associations plusieurs-vers-plusieurs</title>
<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
une(des) colonne(s) d'index.
</para>
<para>
Pour une collection de valeurs, nous utilisons la balise <literal>&lt;element&gt;</literal>.
</para>
<programlistingco>
<areaspec>
<area id="element1b" coords="2 50"/>
<area id="element2b" coords="3 50"/>
<area id="element3b" coords="4 50"/>
</areaspec>
<programlisting><![CDATA[<element
column="nom_de_colonne"
formula="n'importe quelle expression SQL"
type="nomDeType"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="nom-d-element"
/>]]></programlisting>
<calloutlist>
<callout arearefs="element1b">
<para>
<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
</para>
</callout>
<callout arearefs="element3b">
<para>
<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>.
</para>
<programlistingco>
<areaspec>
<area id="manytomany1" coords="2 60"/>
<area id="manytomany2" coords="3 60"/>
<area id="manytomany3" coords="4 60"/>
<area id="manytomany4" coords="5 60"/>
<area id="manytomany5" coords="6 60"/>
<area id="manytomany6" coords="7 60"/>
<area id="manytomany7" coords="8 60"/>
<area id="manytomany8" coords="9 60"/>
</areaspec>
<programlisting><![CDATA[<many-to-many
column="nom_de_colonne"
formula="n'importe quelle expression SQL"
class="NomDeClasse"
fetch="select|join"
unique="true|false"
not-found="ignore|exception"
entity-name="NomDEntite"
property-ref="nomDeProprieteDeLaClasseAssociee"
node="nom-d-element"
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="manytomany1">
<para>
<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
</para>
</callout>
<callout arearefs="manytomany3">
<para>
<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>&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é
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
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>
</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.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Quelques exemples, d'abord, un ensemble de chaînes de caractères :
</para>
<programlisting><![CDATA[<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set>]]></programlisting>
<para>
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"
table="item_sizes"
order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag>]]></programlisting>
<para>
Un tableau d'entités - dans ce cas, une association plusieurs-vers-plusieurs :
</para>
<programlisting><![CDATA[<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array>]]></programlisting>
<para>
Une map de chaînes de caractères vers des dates :
</para>
<programlisting><![CDATA[<map name="holidays"
table="holidays"
schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>]]></programlisting>
<para>
Une liste de composants (discute dans le prochain chapitre) :
</para>
<programlisting><![CDATA[<list name="carComponents"
table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list>]]></programlisting>
</sect2>
<sect2 id="collections-onetomany">
<title>Association un-vers-plusieurs</title>
<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 :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
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
</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
<literal>&lt;one-to-many&gt;</literal> indique que c'est une association un vers plusieurs.
</para>
<programlistingco>
<areaspec>
<area id="onetomany1" coords="2 60"/>
<area id="onetomany2" coords="3 60"/>
<area id="onetomany3" coords="4 60"/>
</areaspec>
<programlisting><![CDATA[<one-to-many
class="NomDeClasse"
not-found="ignore|exception"
entity-name="NomDEntite"
node="nom-d-element"
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="onetomany1">
<para>
<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>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>.
</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.
</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
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.
</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.
</para>
<programlisting><![CDATA[<map name="parts"
cascade="all">
<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map>]]></programlisting>
</sect2>
</sect1>
<sect1 id="collections-advancedmappings">
<title>Mappings de collection avancés</title>
<sect2 id="collections-sorted" revision="2">
<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 :
</para>
<programlisting><![CDATA[<set name="aliases"
table="person_aliases"
sort="natural">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" sort="my.custom.HolidayComparator">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>]]></programlisting>
<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>java.util.Comparator</literal>.
</para>
<para>
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
<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.
</para>
<programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date type="date"/>
</map>]]></programlisting>
<para>
Notez que la valeur de l'attribut <literal>order-by</literal> est un ordre SQL, pas un ordre HQL !
</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.
</para>
<programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
</sect2>
<sect2 id="collections-bidirectional" revision="1">
<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 :
<variablelist>
<varlistentry>
<term>un-vers-plusieurs (NdT : one-to-many)</term>
<listitem>
<para>
ensemble ou sac à une extrémité, une seule valeur à l'autre
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>plusieurs-vers-plusieurs (NdT : many-to-many)</term>
<listitem>
<para>
ensemble ou sac aux deux extrémités
</para>
</listitem>
</varlistentry>
</variablelist>
</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).
</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 :
</para>
<programlisting><![CDATA[<class name="Category">
<id name="id" column="CATEGORY_ID"/>
...
<bag name="items" table="CATEGORY_ITEM">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</bag>
</class>
<class name="Item">
<id name="id" column="CATEGORY_ID"/>
...
<!-- inverse end -->
<bag name="categories" table="CATEGORY_ITEM" inverse="true">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</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
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 :
</para>
<programlisting><![CDATA[
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>
<para>
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>.
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</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 !
</para>
</sect2>
<sect2 id="collections-indexedbidirectional">
<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 :
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children" inverse="true">
<key column="parent_id"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<property name="name"
not-null="true"/>
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</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
pourrions utiliser le mapping suivant :
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children">
<key column="parent_id"
not-null="true"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
insert="false"
update="false"
not-null="true"/>
</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
updates inutiles ?
</para>
</sect2>
<sect2 id="collections-ternary">
<title>Associations ternaires</title>
<para>
Il y a trois approches possibles pour mapper une association ternaire. L'une est d'utiliser
une <literal>Map</literal> avec une association en tant qu'index :
</para>
<programlisting><![CDATA[<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map>]]></programlisting>
<programlisting><![CDATA[<map name="connections">
<key column="incoming_node_id"/>
<map-key-many-to-many column="outgoing_node_id" class="Node"/>
<many-to-many column="connection_id" class="Connection"/>
</map>]]></programlisting>
<para>
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.
</para>
</sect2>
<sect2 id="collections-idbag" revision="1">
<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
des associations plusieurs-vers-plusieurs et des collections de valeurs vers une
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.
</para>
<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
<collection-id column="ID" type="long">
<generator class="sequence"/>
</collection-id>
<key column="PERSON1"/>
<many-to-many column="PERSON2" class="Person" fetch="join"/>
</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.
</para>
<para>
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
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>.
</para>
</sect2>
</sect1>
<!--undocumenting this stuff -->
<!--sect1 id="collections-heterogeneous">
<title>Heterogeneous Associations</title>
<para>
The <literal>&lt;many-to-any&gt;</literal> and <literal>&lt;index-many-to-any&gt;</literal>
elements provide for true heterogeneous associations. These mapping elements work in the
same way as the <literal>&lt;any&gt;</literal> element - and should also be used
rarely, if ever.
</para>
</sect1-->
<sect1 id="collections-example" revision="1">
<title>Exemples de collections</title>
<para>
Les sections précédentes sont assez confuses. Donc prenons un exemple. Cette classe :
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
public class Parent {
private long id;
private Set children;
public long getId() { return id; }
private void setId(long id) { this.id=id; }
private Set getChildren() { return children; }
private void setChildren(Set children) { this.children=children; }
....
....
}]]></programlisting>
<para>
a une collection d'instances de <literal>Child</literal>. Si chaque enfant
a au plus un parent, le mapping le plus naturel est une association
un-vers-plusieurs :
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Ceci mappe les définitions de tables suivantes :
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
<para>
Si le parent est <emphasis>requis</emphasis>, utilisez une association un-vers-plusieurs unidirectionnelle :
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Notez la contrainte <literal>NOT NULL</literal> :
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null
primary key,
name varchar(255),
parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
<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> :
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</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 :
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" table="childset">
<key column="parent_id"/>
<many-to-many class="Child" column="child_id"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Définitions des tables :
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null,
child_id bigint not null,
primary key ( parent_id, child_id ) )
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
see <xref linkend="example-parentchild"/>.
</para>
<para>
Des mappings d'association plus exotiques sont possibles, nous cataloguerons toutes les possibilités
dans le prochain chapitre.
</para>
</sect1>
</chapter>