hibernate-orm/doc/reference/fr/modules/inheritance_mapping.xml

484 lines
20 KiB
XML
Raw Normal View History

<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="inheritance">
<title>Mapping d'h<>ritage de classe</title>
<sect1 id="inheritance-strategies" revision="3">
<title>Les trois strat<61>gies</title>
<para>
Hibernate supporte les trois strat<61>gies d'h<>ritage de base :
</para>
<itemizedlist>
<listitem>
<para>
une table par hi<68>rarchie de classe (table per class hierarchy)
</para>
</listitem>
<listitem>
<para>
une table par classe fille (table per subclass)
</para>
</listitem>
<listitem>
<para>
une table par classe concr<63>te (table per concrete class)
</para>
</listitem>
</itemizedlist>
<para>
Hibernate supporte en plus une quatri<72>mestrat<61>gie, l<>g<EFBFBD>rement diff<66>rente, qui supporte le polymorphisme :
</para>
<itemizedlist>
<listitem>
<para>
le polymorphisme implicite
</para>
</listitem>
</itemizedlist>
<para>
Il est possible d'utiliser diff<66>rentes strat<61>gies de mapping pour diff<66>rentes branches d'une m<>me
hi<68>rarchie d'h<>ritage, et alors d'employer le polymorphisme implicite pour r<>aliser le
polymorphisme <20> travers toute la hi<68>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 <20>l<EFBFBD>ment <literal>&lt;class&gt;</literal> racine.
Il est possible de m<>langer ensemble les strat<61>gies d'une table par hi<68>rarchie et d'une
table par sous-classe, pour le m<>me <20>l<EFBFBD>ment <literal>&lt;class&gt;</literal>, en combinant
les <20>l<EFBFBD>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<61>s, directement sous
<literal>hibernate-mapping</literal>. Ceci vous permet d'<27>tendre une hi<68>rarchie de classe juste en
ajoutant un nouveau fichier de mapping. Vous devez sp<73>cifier un attribut <literal>extends</literal>
dans le mapping de la sous-classe, en nommant une super-classe pr<70>c<EFBFBD>demment mapp<70>e. Note :
pr<70>c<EFBFBD>demment cette foncionnalit<69> 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 <20> l'int<6E>rieur d'un simple fichier de mapping impose encore de d<>finir les classes m<>res
avant les classes filles.
</para>
<programlisting><![CDATA[
<hibernate-mapping>
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</hibernate-mapping>]]></programlisting>
<sect2 id="inheritance-tableperclass" >
<title>Une table par hi<68>rarchie de classe</title>
<para>
Supposons que nous ayons une interface <literal>Payment</literal>, impl<70>ment<6E>e
par <literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
<literal>ChequePayment</literal>. La strat<61>gie une table par hi<68>rarchie serait :
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
Une seule table est requise. Une grande limitation de cette
strat<61>gie est que les colonnes d<>clar<61>es par les classes filles, telles que <literal>CCTYPE</literal>,
ne peuvent avoir de contrainte <literal>NOT NULL</literal>.
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>Une table par classe fille</title>
<para>
La strat<61>gie une table par classe fille serait :
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
Quatre tables sont requises. Les trois tables des classes filles ont
une cl<63> primaire associ<63>e <20> la table classe m<>re (le mod<6F>le relationnel
est une association un-vers-un).
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
<title>Une table par classe fille, en utilisant un discriminant</title>
<para>
Notez que l'impl<70>mentation Hibernate de la strat<61>gie un table par
classe fille ne n<>cessite pas de colonne discriminante dans la table
classe m<>re. D'autres impl<70>mentations de mappers Objet/Relationnel utilisent
une autre impl<70>mentation de la strat<61>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 <20> impl<70>menter mais plus correcte
d'une point de vue relationnel. Si vous aimeriez utiliser
une colonne discriminante avec la strat<61>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>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
La d<>claration optionnelle <literal>fetch="select"</literal> indique <20> Hibernate
de ne pas r<>cup<75>rer les donn<6E>es de la classe fille <literal>ChequePayment</literal> par une jointure externe lors des requ<71>tes sur la classe m<>re.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>M<EFBFBD>lange d'une table par hi<68>rarchie de classe avec une table par classe fille</title>
<para>
Vous pouvez m<>me m<>langer les strat<61>gies d'une table par hi<68>rarchie de classe et d'une table par classe fille en utilisant cette approche :
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
Pour importe laquelle de ces strat<61>gies, une association polymorphique vers la classe racine
<literal>Payment</literal> est mapp<70>e en utilisant <literal>&lt;many-to-one&gt;</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
</sect2>
<sect2 id="inheritance-tableperconcrete" revision="2">
<title>Une table par classe concr<63>te</title>
<para>
Il y a deux mani<6E>res d'utiliser la strat<61>gie d'une table par classe concr<63>te. La premi<6D>re
est d'employer <literal>&lt;union-subclass&gt;</literal>.
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
Trois tables sont n<>cessaires pour les classes filles. Chaque table d<>finit des colonnes
pour toutes les propri<72>t<EFBFBD>s de la classe, incluant les propri<72>t<EFBFBD>s h<>rit<69><74>s.
</para>
<para>
La limitation de cette approche est que si une propri<72>t<EFBFBD> est mapp<70>e sur la classe m<>re, le nom
de la colonne doit <20>tre le m<>me pour toutes les classes filles. (Nous pourrions <20>tre plus souple
dans une future version d'Hibernate).
La strat<61>gie du g<>n<EFBFBD>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 <20>tre partag<61>e par toutes les classes filles "union" d'une hi<68>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<70>mentaire (par d<>faut,
<literal>PAYMENT</literal> dans l'exemple ci-dessus) est requise pour contenir des instances
de la classe m<>re.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Une table par classe concr<63>te, en utilisant le polymorphisme implicite</title>
<para>
Une approche alternative est l'emploi du polymorphisme implicite :
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
Notez que nulle part nous ne mentionnons l'interface <literal>Payment</literal> explicitement.
Notez aussi que des propri<72>t<EFBFBD>s de <literal>Payment</literal> sont mapp<70>es dans
chaque classe fille. Si vous voulez <20>viter des duplications, consid<69>rez l'utilisation des
entit<69>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<6E>nient de cette approche est qu'Hibernate ne g<>n<EFBFBD>re pas d'<literal>UNION</literal>s SQL
lors de l'ex<65>cution des requ<71>tes polymorphiques.
</para>
<para>
Pour cette strat<61>gie de mapping, une association polymorphique pour <literal>Payment</literal>
est habituellement mapp<70>e en utilisant <literal>&lt;any&gt;</literal>.
</para>
<programlisting><![CDATA[<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>]]></programlisting>
</sect2>
<sect2 id="inheritace-mixingpolymorphism">
<title>M<EFBFBD>lange du polymorphisme implicite avec d'autres mappings d'h<>ritage</title>
<para>
Il y a une chose suppl<70>mentaire <20> noter <20> propos de ce mapping. Puisque les classes filles sont
chacune mapp<70>es avec leur propre <20>l<EFBFBD>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<68>rarchie
d'h<>ritage ! (Et vous pouvez encore faire des requ<71>tes polymorphiques pour l'interface <literal>Payment</literal>).
</para>
<programlisting><![CDATA[<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>]]></programlisting>
<para>
Encore une fois, nous ne mentionnons pas explicitement <literal>Payment</literal>.
Si nous ex<65>cutons une requ<71>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<70>mentent aussi <literal>Payment</literal>),
<literal>CashPayment</literal> et <literal>ChequePayment</literal> mais pas
les instances de <literal>NonelectronicTransaction</literal>.
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>Limitations</title>
<para>
Il y a certaines limitations <20> l'approche du "polymorphisme implicite"
pour la strat<61>gie de mapping d'une table par classe concr<63>te.
Il y a plut<75>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<63>te, et du polymorphisme implicite, dans Hibernate.
</para>
<table frame="topbot">
<title>Caract<EFBFBD>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*"/>
<colspec colname='c3' colwidth="1*"/>
<colspec colname='c4' colwidth="1*"/>
<colspec colname='c5' colwidth="1*"/>
<colspec colname='c6' colwidth="1*"/>
<colspec colname='c7' colwidth="1*"/>
<colspec colname='c8' colwidth="1*"/>
<thead>
<row>
<entry>Strat<EFBFBD>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<EFBFBD>tes polymorphiques</entry>
<entry>Jointures polymorphiques</entry>
<entry>R<EFBFBD>cup<EFBFBD>ration par jointure externe</entry>
</row>
</thead>
<tbody>
<row>
<entry>une table par hi<68>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>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<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<EFBFBD>e</emphasis></entry>
</row>
<row>
<entry>une table par classe fille</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>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<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<EFBFBD>e</emphasis></entry>
</row>
<row>
<entry>une table par classe concr<63>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>
<entry><literal>&lt;many-to-many&gt;</literal></entry>
<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<EFBFBD>e</emphasis></entry>
</row>
<row>
<entry>une table par classe concr<63>te (polymorphisme implicite)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>non support<72></emphasis></entry>
<entry><emphasis>non support<72></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<72>es</emphasis></entry>
<entry><emphasis>non support<72>e</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>