Hibernate.org Community Documentation

- Précédent
- Suivant

## Chapitre 10. Mapping d'héritage de classe

## 10.1. Les trois stratégies

Hibernate supporte les trois stratégies d'héritage de base :

- une table par hiérarchie de classe

- table per subclass

- une table par classe concrète

Par ailleurs, Hibernate supporte une quatrième stratégie, avec un polymorphisme légèrement différent :

- le polymorphisme implicite

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 ensuite d'employer le polymorphisme implicite pour réaliser le polymorphisme à travers toute la hiérarchie. Toutefois, Hibernate ne supporte pas les mélanges de mappages `<subclass>`, `<joined-subclass>` et `<union-subclass>` pour le même élément `<class>` racine. Il est possible de mélanger les stratégies d'une table par hiérarchie et d'une table par sous-classe, pour le même élément `<class>`, en combinant les éléments `<subclass>` et `<join>` (voir ci-dessous).

Il est possible de définir des mappages de `subclass`, `union-subclass`, et `joined-subclass` dans des documents de mappage séparés, directement sous `hibernate-mappage`. Ceci vous permet d'étendre une hiérarchie de classe juste en ajoutant un nouveau fichier de mappage. Vous devez spécifier un attribut `extends` dans le mappage de la sous-classe, en nommant une super-classe précédemment mappée. Note : précédemment cette fonctionnalité rendait important l'ordre des documents de mappage. Depuis Hibernate3, l'ordre des fichier de mappage n'importe plus lors de l'utilisation du mot-clef "extends". L'ordre à l'intérieur d'un simple fichier de mappage impose encore de définir les classes mères avant les classes filles.

     <hibernate-mapping>

         <subclass name="DomesticCat" extends="Cat" discriminator-value="D">

              <property name="name" type="string"/>

         </subclass>

     </hibernate-mapping

    >

### 10.1.1. Une table par hiérarchie de classe

Supposons que nous ayons une interface `Payment`, implémentée par `CreditCardPayment`, `CashPayment`, `ChequePayment`. La stratégie une table par hiérarchie serait :

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="PAYMENT_TYPE" type="string"/>

        <property name="amount" column="AMOUNT"/>

        ...

        <subclass name="CreditCardPayment" discriminator-value="CREDIT">

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </subclass>

        <subclass name="CashPayment" discriminator-value="CASH">

            ...

        </subclass>

        <subclass name="ChequePayment" discriminator-value="CHEQUE">

            ...

        </subclass>

    </class

    >

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 `CCTYPE`, peuvent ne pas avoir de contrainte `NOT NULL`.

### 10.1.2. Une table par classe fille

Une table par classe-fille de mappage serait :

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="AMOUNT"/>

        ...

        <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">

            <key column="PAYMENT_ID"/>

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </joined-subclass>

        <joined-subclass name="CashPayment" table="CASH_PAYMENT">

            <key column="PAYMENT_ID"/>

            ...

        </joined-subclass>

        <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

            <key column="PAYMENT_ID"/>

            ...

        </joined-subclass>

    </class

    >

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 est une association un-à-un).

### 10.1.3. Une table par classe fille, en utilisant un discriminant

Notez que l'implémentation Hibernate de la stratégie une 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 pouvez combiner l'utilisation de `<subclass>` et `<join>`, comme suit :

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="PAYMENT_TYPE" type="string"/>

        <property name="amount" column="AMOUNT"/>

        ...

        <subclass name="CreditCardPayment" discriminator-value="CREDIT">

            <join table="CREDIT_PAYMENT">

                <key column="PAYMENT_ID"/>

                <property name="creditCardType" column="CCTYPE"/>

                ...

            </join>

        </subclass>

        <subclass name="CashPayment" discriminator-value="CASH">

            <join table="CASH_PAYMENT">

                <key column="PAYMENT_ID"/>

                ...

            </join>

        </subclass>

        <subclass name="ChequePayment" discriminator-value="CHEQUE">

            <join table="CHEQUE_PAYMENT" fetch="select">

                <key column="PAYMENT_ID"/>

                ...

            </join>

        </subclass>

    </class

    >

La déclaration optionnelle `fetch="select"` indique à Hibernate de ne pas récupérer les données de la classe fille `ChequePayment` par une jointure externe lors des requêtes sur la classe mère.

### 10.1.4. Mélange d'une table par hiérarchie de classe avec une table par classe fille

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 :

    <class name="Payment" table="PAYMENT">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="PAYMENT_TYPE" type="string"/>

        <property name="amount" column="AMOUNT"/>

        ...

        <subclass name="CreditCardPayment" discriminator-value="CREDIT">

            <join table="CREDIT_PAYMENT">

                <property name="creditCardType" column="CCTYPE"/>

                ...

            </join>

        </subclass>

        <subclass name="CashPayment" discriminator-value="CASH">

            ...

        </subclass>

        <subclass name="ChequePayment" discriminator-value="CHEQUE">

            ...

        </subclass>

    </class

    >

Pour importe laquelle de ces stratégies, une association polymorphique vers la classe racine `Payment` est mappée en utilisant `<many-to-one>`.

    <many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

### 10.1.5. Une table par classe concrète

Il y a deux manières d'utiliser la stratégie d'une table par classe concrète. La première est d'employer `<union-subclass>`.

    <class name="Payment">

        <id name="id" type="long" column="PAYMENT_ID">

            <generator class="sequence"/>

        </id>

        <property name="amount" column="AMOUNT"/>

        ...

        <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">

            <property name="creditCardType" column="CCTYPE"/>

            ...

        </union-subclass>

        <union-subclass name="CashPayment" table="CASH_PAYMENT">

            ...

        </union-subclass>

        <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

            ...

        </union-subclass>

    </class

    >

Trois tables sont nécessaires pour les classes filles. Chaque table définit des colonnes pour toutes les propriétés de la classe, y compris les propriétés héritées.

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 (Une future version de Hibernate pourra assouplir ce comportement). 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 de graine de la clef primaire doit être partagée par toutes les classes filles fusionnées d'une hiérarchie.

Si votre classe mère est abstraite, mappez la avec `abstract="true"`. Bien sûr, si elle n'est pas abstraite, une table supplémentaire (par défaut, `PAYMENT` dans l'exemple ci-dessus) est requise pour contenir des instances de la classe mère.

### 10.1.6. Une table par classe concrète, en utilisant le polymorphisme implicite

Une approche alternative est l'emploi du polymorphisme implicite :

    <class name="CreditCardPayment" table="CREDIT_PAYMENT">

        <id name="id" type="long" column="CREDIT_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="CREDIT_AMOUNT"/>

        ...

    </class>

    <class name="CashPayment" table="CASH_PAYMENT">

        <id name="id" type="long" column="CASH_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="CASH_AMOUNT"/>

        ...

    </class>

    <class name="ChequePayment" table="CHEQUE_PAYMENT">

        <id name="id" type="long" column="CHEQUE_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <property name="amount" column="CHEQUE_AMOUNT"/>

        ...

    </class

    >

Notice that the `Payment` interface is not mentioned explicitly. Also notice that properties of `Payment` are mapped in each of the subclasses. If you want to avoid duplication, consider using XML entities (for example, `[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]` in the `DOCTYPE` declaration and `&allproperties;` in the mapping).

L'inconvénient de cette approche est que Hibernate ne génère pas de SQL `UNION` s lors de l'exécution des requêtes polymorphiques.

Pour cette stratégie de mappage, une association polymorphique pour `Payment` est habituellement mappée en utilisant `<any>`.

    <any name="payment" meta-type="string" id-type="long">

        <meta-value value="CREDIT" class="CreditCardPayment"/>

        <meta-value value="CASH" class="CashPayment"/>

        <meta-value value="CHEQUE" class="ChequePayment"/>

        <column name="PAYMENT_CLASS"/>

        <column name="PAYMENT_ID"/>

    </any

    >

### 10.1.7. Mélange du polymorphisme implicite avec d'autres mappages d'héritage

Il y a une chose supplémentaire à noter à propos de ce mappage. Puisque les classes filles sont chacune mappées avec leur propre élément `<class>` (et puisque `Payment` 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 `Payment`).

    <class name="CreditCardPayment" table="CREDIT_PAYMENT">

        <id name="id" type="long" column="CREDIT_PAYMENT_ID">

            <generator class="native"/>

        </id>

        <discriminator column="CREDIT_CARD" type="string"/>

        <property name="amount" column="CREDIT_AMOUNT"/>

        ...

        <subclass name="MasterCardPayment" discriminator-value="MDC"/>

        <subclass name="VisaPayment" discriminator-value="VISA"/>

    </class>

    <class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">

        <id name="id" type="long" column="TXN_ID">

            <generator class="native"/>

        </id>

        ...

        <joined-subclass name="CashPayment" table="CASH_PAYMENT">

            <key column="PAYMENT_ID"/>

            <property name="amount" column="CASH_AMOUNT"/>

            ...

        </joined-subclass>

        <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

            <key column="PAYMENT_ID"/>

            <property name="amount" column="CHEQUE_AMOUNT"/>

            ...

        </joined-subclass>

    </class

    >

Encore une fois, nous ne mentionnons pas explicitement `Payment`. Si nous exécutons une requête sur l'interface `Payment` - par exemple, `from Payment` - Hibernate retournera automatiquement les instances de `CreditCardPayment` (et ses classes filles puisqu'elles implémentent aussi `Payment`), `CashPayment` et `ChequePayment` mais pas les instances de `NonelectronicTransaction`.

## 10.2. Limitations

Il y a certaines limitations à l'approche du "polymorphisme implicite" pour la stratégie de mappage d'une table par classe concrète. Il y a plutôt moins de limitations restrictives aux mappages `<union-subclass>`.

The following table shows the limitations of table per concrete-class mappings, and of implicit polymorphism, in Hibernate.

Tableau 10.1. Features of inheritance mappings

Inheritance strategy | Polymorphic many-to-one | Polymorphic one-to-one | Polymorphic one-to-many | Polymorphic many-to-many | Polymorphic `load()/get()` | Polymorphic queries | Polymorphic joins | Outer join fetching
--- | --- | --- | --- | --- | --- | --- | --- | ---
table per class-hierarchy | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
table per subclass | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
une table par classe concrète (union-classe fille) | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` (for `inverse="true"` only) | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
table per concrete class (implicit polymorphism) | `<any>` | not supported | not supported | `<many-to-any>` | `s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()` | `from Payment p` | not supported | not supported

Copyright © 2004 Red Hat, Inc.

- PrécédentChapitre 9. Mappage de composants
- Niveau supérieur
- Sommaire
- SuivantChapitre 11. Travailler avec des objets