Hibernate.org Community Documentation

- Précédent
- Suivant

## Chapitre 9. Mappage de composants

La notion de composants est réutilisée dans différents contextes, avec différents objectifs, à travers Hibernate.

## 9.1. Objets dépendants

Le composant est un objet inclus dans un autre objet, sauvegardé en tant que type valeur, et non en tant que référence entité. Le terme "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éliser l'objet personne de la façon suivante :

    public class Person {

        private java.util.Date birthday;

        private Name name;

        private String key;

        public String getKey() {

            return key;

        }

        private void setKey(String key) {

            this.key=key;

        }

        public java.util.Date getBirthday() {

            return birthday;

        }

        public void setBirthday(java.util.Date birthday) {

            this.birthday = birthday;

        }

        public Name getName() {

            return name;

        }

        public void setName(Name name) {

            this.name = name;

        }

        ......

        ......

    }

    public class Name {

        char initial;

        String first;

        String last;

        public String getFirst() {

            return first;

        }

        void setFirst(String first) {

            this.first = first;

        }

        public String getLast() {

            return last;

        }

        void setLast(String last) {

            this.last = last;

        }

        public char getInitial() {

            return initial;

        }

        void setInitial(char initial) {

            this.initial = initial;

        }

    }

Maintenant `Name` pourra être sauvegardé en tant que composant de `Person`. Remarquez que `Name` définit des méthodes getter et setter pour ses propriétés persistantes, mais ne doit déclarer aucune interface ou propriété d'identification.

Dans Hibernate le mappage du composant serait :

    <class name="eg.Person" table="person">

        <id name="Key" column="pid" type="string">

            <generator class="uuid"/>

        </id>

        <property name="birthday" type="date"/>

        <component name="Name" class="eg.Name"

    > <!-- class attribute optional -->

            <property name="initial"/>

            <property name="first"/>

            <property name="last"/>

        </component>

    </class

    >

La table "person" aurait les colonnes `pid`, `birthday`, `initial`, `first` et `last`.

Comme tous les types valeurs, les composants ne supportent pas les références partagées. En d'autres termes, 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 sémantique de la valeur null d'un composant est ad hoc. Quand il recharge l'objet qui contient le composant, Hibernate suppose que si toutes les colonnes de composants sont nulles, le composant est positionné à la valeur null. Ce choix programmatif devrait être satisfaisant dans la plupart des cas.

Les propriétés d'un composant peuvent être de tous les types habituellement supportés par Hibernate (collections, associations plusieurs-à-un, autres composants, etc). Les composants imbriqués ne doivent pas être vus comme quelque chose d'exotique. Hibernate a été conçu pour supporter un modèle d'objet finement granulé.

L'élément `<component>` permet de déclarer un sous-élément `<parent>` qui associe une propriété de la classe composant comme une référence arrière vers l'entité contenante.

    <class name="eg.Person" table="person">

        <id name="Key" column="pid" type="string">

            <generator class="uuid"/>

        </id>

        <property name="birthday" type="date"/>

        <component name="Name" class="eg.Name" unique="true">

            <parent name="namedPerson"/> <!-- reference back to the Person -->

            <property name="initial"/>

            <property name="first"/>

            <property name="last"/>

        </component>

    </class

    >

## 9.2. Collection d'objets dépendants

Les collections d'objets dépendants sont supportées (exemple: un tableau de type `Name`). Déclarez votre collection de composants en remplaçant la balise `<element>` par la balise `<composite-element>` :

    <set name="someNames" table="some_names" lazy="true">

        <key column="id"/>

        <composite-element class="eg.Name"

    > <!-- class attribute required -->

            <property name="initial"/>

            <property name="first"/>

            <property name="last"/>

        </composite-element>

    </set

    >

## Important

Remarque : si vous définissez un `Set` d'éléments composites, il est très important d'implémenter les méthodes `equals()` et `hashCode()` correctement.

Les éléments composites peuvent aussi contenir des composants mais pas des collections. Si votre élément composite contient aussi des composants, utilisez la balise `<nested-composite-element>`. Une collection de composants qui contiennent 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ée. Essayez de remodeler votre élément composite comme une entité - remarquez que si le modèle Java est le même, toutefois le modèle relationnel et la sémantique de persistance diffèrent quelque peu.

Remarquez que le mappage d'éléments composites ne supporte pas la nullité des propriétés lorsqu'on utilise un `<set>`. Hibernate lorsqu'il supprime un objet, utilise chaque colonne pour identifier un objet (il n'y a pas de colonne distincte de clés primaires dans la 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 : `<list>`, `<map>`, `<bag>` ou `<idbag>`.

Un cas particulier d'élément composite est un élément composite qui inclut un élément imbriqué `<many-to-one>`. Un mappage comme celui-ci vous permet d'associer des colonnes supplémentaires d'une table d'association plusieurs à plusieurs à la classe de l'élément composite. L'exemple suivant est une association plusieurs à plusieurs de `Order` à `Item` où `purchaseDate`, `price` et `quantity` sont des propriétés de l'association :

    <class name="eg.Order" .... >

        ....

        <set name="purchasedItems" table="purchase_items" lazy="true">

            <key column="order_id">

            <composite-element class="eg.Purchase">

                <property name="purchaseDate"/>

                <property name="price"/>

                <property name="quantity"/>

                <many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->

            </composite-element>

        </set>

    </class

    >

Par ailleurs, on ne peut évidemment pas faire référence à l'achat (purchase), pour pouvoir naviguer de façon bidirectionnelle dans l'association. N'oubliez pas que les composants sont de type valeurs et n'autorisent pas les références partagées. Un `Purchase` unique peut être dans le set d'un `Order`, mais ne peut pas être référencé par `Item` simultanément.

Même les associations ternaires, quaternaires ou autres sont possibles :

    <class name="eg.Order" .... >

        ....

        <set name="purchasedItems" table="purchase_items" lazy="true">

            <key column="order_id">

            <composite-element class="eg.OrderLine">

                <many-to-one name="purchaseDetails class="eg.Purchase"/>

                <many-to-one name="item" class="eg.Item"/>

            </composite-element>

        </set>

    </class

    >

Des éléments composites peuvent apparaître dans les requêtes en utilisant la même syntaxe que les associations vers d'autres entités.

## 9.3. Les composants en tant qu'indices de Map

L'élément `<composite-map-key>` vous permet de mapper une classe de composant comme indice d'une `Map`. Assurez-vous de surcharger correctement `hashCode()` et `equals()` dans la classe du composant.

## 9.4. Les composants en tant qu'identifiants composites

Vous pouvez utiliser un composant comme identifiant d'une classe entité. À cet effet, votre classe de composant doit respecter certaines exigences :

- Elle doit implémenter `java.io.Serializable`.

- Elle doit redéfinir `equals()` et `hashCode()`, de façon cohérente avec la notion d'égalité de clé composite de la base de données.

## Remarque

Avec Hibernate3, la seconde exigence n'est plus absolument nécessaire, néanmoins continuez de l'effectuer.

Vous ne pouvez pas utiliser de `IdentifierGenerator` pour générer des clés composites, par contre l'application doit assigner ses propres identifiants.

Utiliser la balise `<composite-id>` (avec les éléments imbriqués `<key-property>`) à la place de l'habituel déclaration `<id>`. Par exemple, la classe `OrderLine` possède une clé primaire qui dépend de la clé primaire (composite) de `Order`.

    <class name="OrderLine">

        <composite-id name="id" class="OrderLineId">

            <key-property name="lineId"/>

            <key-property name="orderId"/>

            <key-property name="customerId"/>

        </composite-id>

        <property name="name"/>

        <many-to-one name="order" class="Order"

                insert="false" update="false">

            <column name="orderId"/>

            <column name="customerId"/>

        </many-to-one>

        ....

    </class

    >

Toutes les clés étrangères référençant la table `OrderLine` sont également composites. Vous devez en tenir compte lorsque vous écrivez vos mappage d'association pour les autres classes. Une association à `OrderLine` sera mappée de la façon suivante :

    <many-to-one name="orderLine" class="OrderLine">

    <!-- the "class" attribute is optional, as usual -->

        <column name="lineId"/>

        <column name="orderId"/>

        <column name="customerId"/>

    </many-to-one

    >

## Astuce

The `column` element is an alternative to the `column` attribute everywhere. Using the `column` element just gives more declaration options, which are mostly useful when utilizing `hbm2ddl`

Une association `plusieurs-à-plusieurs` à `OrderLine` utilisera aussi une clé étrangère composite :

    <set name="undeliveredOrderLines">

        <key column name="warehouseId"/>

        <many-to-many class="OrderLine">

            <column name="lineId"/>

            <column name="orderId"/>

            <column name="customerId"/>

        </many-to-many>

    </set

    >

La collection des `OrderLine` s dans `Order` utilisera :

    <set name="orderLines" inverse="true">

        <key>

            <column name="orderId"/>

            <column name="customerId"/>

        </key>

        <one-to-many class="OrderLine"/>

    </set

    >

Comme d'habitude, l'élément `<one-to-many>` ne déclare pas de colonne.

Si `OrderLine` lui-même possède une collection, il possédera de même une clé composite étrangère.

    <class name="OrderLine">

        ....

        ....

        <list name="deliveryAttempts">

            <key

    >   <!-- a collection inherits the composite key type -->

                <column name="lineId"/>

                <column name="orderId"/>

                <column name="customerId"/>

            </key>

            <list-index column="attemptId" base="1"/>

            <composite-element class="DeliveryAttempt">

                ...

            </composite-element>

        </set>

    </class

    >

## 9.5. Les composants dynamiques

Vous pouvez également mapper une propriété de type `Map` :

    <dynamic-component name="userAttributes">

        <property name="foo" column="FOO" type="string"/>

        <property name="bar" column="BAR" type="integer"/>

        <many-to-one name="baz" class="Baz" column="BAZ_ID"/>

    </dynamic-component

    >

La sémantique de l'association à un `<dynamic-component>` est identique à celle que l'on utilise pour le `<component>`. L'avantage de ce type de mappage est qu'il permet de déterminer les véritables propriétés du bean au moment du déploiement, en éditant simplement le document de mappage. La manipulation du document de mappage pendant l'exécution de l'application est aussi possible en utilisant un parser DOM. Il y a même mieux, vous pouvez accéder (et changer) le métamodèle de configuration-temps de Hibernate en utilisant l'objet `Configuration`.

Copyright © 2004 Red Hat, Inc.

- PrécédentChapitre 8. Mapper les associations
- Niveau supérieur
- Sommaire
- SuivantChapitre 10. Mapping d'héritage de classe