Hibernate.orgCommunity Documentation

Chapitre 8. Mapper les associations

8.1. Introduction
8.2. Associations unidirectionnelles
8.2.1. plusieurs-à-un
8.2.2. Un-à-un
8.2.3. un-à-plusieurs
8.3. Associations unidirectionnelles avec tables de jointure
8.3.1. un-à-plusieurs
8.3.2. plusieurs-à-un
8.3.3. Un-à-un
8.3.4. Plusieurs-à-plusieurs
8.4. Associations bidirectionnelles
8.4.1. un-à-plusieurs / plusieurs-à-un
8.4.2. Un-à-un
8.5. Associations bidirectionnelles avec tables de jointure
8.5.1. un-à-plusieurs / plusieurs-à-un
8.5.2. un-à-un
8.5.3. Plusieurs-à-plusieurs
8.6. Des mappages d'associations plus complexes

Mapper les associations correctement, est souvent la tâche la plus difficile. Dans cette section, nous traiterons les cas classiques, un par un, en commençant par les mappages unidirectionnels, puis nous aborderons la question des mappages bidirectionnels. Nous illustrons tous nos exemples avec les classes Person et Address.

Nous classifions les associations selon qu'elles sont ou non bâties sur une table de jointure supplémentaire et sur la multiplicité.

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, et 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.

Une association bidirectionnelle plusieurs-à-un est le type d'association que l'on rencontre le plus fréquemment. L'exemple suivant illustre la façon standard de créer des relations parents/enfants.


<class name="Person">
    <id name="id" column="personId">
        <generator class="native"/>
    </id>
    <many-to-one name="address" 
        column="addressId"
        not-null="true"/>
</class>

<class name="Address">
    <id name="id" column="addressId">
        <generator class="native"/>
    </id>
    <set name="people" inverse="true">
        <key column="addressId"/>
        <one-to-many class="Person"/>
    </set>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
        

Si vous utilisez une List, ou toute autre collection indexée, vous devez paramétrer la colonne key de la clé étrangère à not null, 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 update="false" et insert="false") :


<class name="Person">
   <id name="id"/>
   ...
   <many-to-one name="address"
      column="addressId"
      not-null="true"
      insert="false"
      update="false"/>
</class>

<class name="Address">
   <id name="id"/>
   ...
   <list name="people">
      <key column="addressId" not-null="true"/>
      <list-index column="peopleIdx"/>
      <one-to-many class="Person"/>
   </list>
</class
>

Il est important de définir not-null="true sur l'élément <key> du mapping de la collection si la colonne de clé étrangère sous-jacente est NOT NULL. Ne déclarez pas seulement not-null="true" sur un élément imbriqué possible<column>, mais sur l'élément <key>.

Des associations encore plus complexes sont extrêmement rares. Hibernate permet de gérer des situations plus complexes en utilisant des extraits SQL embarqués dans le fichier de mapping. Par exemple, si une table avec des informations historiques sur un compte définit les colonnes accountNumber, effectiveEndDate et effectiveStartDate, elle sera mappée de la façon suivante :


<properties name="currentAccountKey">
    <property name="accountNumber" type="string" not-null="true"/>
    <property name="currentAccount" type="boolean">
        <formula
>case when effectiveEndDate is null then 1 else 0 end</formula>
    </property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>

Nous pouvons mapper une association à l'instance courante, celle avec une effectiveEndDate nulle, en utilisant :


<many-to-one name="currentAccountInfo"
        property-ref="currentAccountKey"
        class="AccountInfo">
    <column name="accountNumber"/>
    <formula
>'1'</formula>
</many-to-one
>

Dans un exemple plus complexe, imaginez qu'une association entre Employee et Organization soit gérée dans une table Employment pleine de données historiques. Dans ce cas, une association vers l'employeur le plus récent (celui avec la startDate (date de commencement de travail la plus récente) pourrait être mappée comme suit :


<join>
    <key column="employeeId"/>
    <subselect>
        select employeeId, orgId 
        from Employments 
        group by orgId 
        having startDate = max(startDate)
    </subselect>
    <many-to-one name="mostRecentEmployer" 
            class="Organization" 
            column="orgId"/>
</join
>

Vous pouvez être créatif grâce à ces possibilités, mais il est généralement plus pratique de gérer ce genre de cas en utilisant des requêtes HQL ou par critère.