hibernate-orm/reference/es/modules/inheritance_mapping.xml

465 lines
19 KiB
XML

<chapter id="inheritance">
<title>Mapeo de Herencia</title>
<sect1 id="inheritance-strategies" revision="2">
<title>Las Tres Estrategias</title>
<para>
Hibernate soporta las tres estrategias b&#x00e1;sicas de mapeo de herencia:
</para>
<itemizedlist>
<listitem>
<para>
tabla por jerarqu&#x00ed;a de clases
</para>
</listitem>
<listitem>
<para>
tabla por subclase
</para>
</listitem>
<listitem>
<para>
tabla por clase concreta
</para>
</listitem>
</itemizedlist>
<para>
En adici&#x00f3;n, Hibernate soporta un cuarto, ligeramente diferente tipo
de polimorfismo:
</para>
<itemizedlist>
<listitem>
<para>
polimorfismo impl&#x00ed;cito
</para>
</listitem>
</itemizedlist>
<para>
Es posible usar estrategias de mapeo diferentes para diferentes
ramificaciones de la misma jerarqu&#x00ed;a de herencia, y entonces usar
polimorfismo impl&#x00ed;cito para conseguir polimorfismo a trav&#x00e9;s de
toda la jerarqu&#x00ed;a. Sin embargo, Hibernate no soporta la mezcla de
mapeos <literal>&lt;subclass&gt;</literal>,
y <literal>&lt;joined-subclass&gt;</literal>
y <literal>&lt;union-subclass&gt;</literal> bajo el mismo elemento
<literal>&lt;class&gt;</literal> ra&#x00ed;z. Es posible mezclar juntas las
estrategias de tabla por jerarqu&#x00ed;a y tabla por subclase, bajo el mismo
elemento <literal>&lt;class&gt;</literal>, combinando los elementos
<literal>&lt;subclass&gt;</literal> y <literal>&lt;join&gt;</literal>
(ver debajo).
</para>
<sect2 id="inheritance-tableperclass" >
<title>Tabla por jerarqu&#x00ed;a de clases</title>
<para>
Sup&#x00f3;n que tenemos una interface <literal>Payment</literal>, con
los implementadores <literal>CreditCardPayment</literal>,
<literal>CashPayment</literal>, <literal>ChequePayment</literal>.
El mapeo de tabla por jerarqu&#x00ed;a se ver&#x00ed;a as&#x00ed;:
</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>
Se requiere exactamente una tabla. Hay una gran limitaci&#x00f3;n de esta estrategia de mapeo:
las columnas declaradas por las subclases, como <literal>CCTYPE</literal>, no pueden
tener restricciones <literal>NOT NULL</literal>.
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass">
<title>Tabla por subclase</title>
<para>
Un mapeo de tabla por sublclase se ver&#x00ed;a as&#x00ed;:
</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>
Se requieren cuatro tablas. Las tres tablas de subclase tienen
asociaciones de clave primaria a la tabla de superclase (de modo
que en el modelo relacional es realmente una asociaci&#x00f3;n uno-a-uno).
</para>
</sect2>
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
<title>Tabla por subclase, usando un discriminador</title>
<para>
Observa que la implementaci&#x00f3;n de Hibernate de tabla por subclase
no requiere ninguna columna discriminadora. Otros mapeadores
objeto/relacional usan una implementaci&#x00f3;n diferente de tabla por
subclase que requiere una columna discriminadora de tipo en la tabla
de superclase. Este enfoque es mucho m&#x00e1;s dif&#x00ed;cil de implementar
pero discutiblemente m&#x00e1;s correcto desde un punto de vista relacional.
Si quisieras usar una columna discriminadora con la estrategia de
tabla por subclase, puedes combinar el uso de <literal>&lt;subclass&gt;</literal>
y <literal>&lt;join&gt;</literal>, como sigue:
</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 declaraci&#x00f3;n opcional <literal>fetch="select"</literal> dice a Hibernate
que no recupere los datos de la subclase <literal>ChequePayment</literal>
usando una uni&#x00f3;n externa (outer join) al consultar la superclase.
</para>
</sect2>
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
<title>Mezclando tabla por jerarqu&#x00ed;a de clases con tabla por subclase</title>
<para>
Puedes incluso mezclar las estrategias de tabla po jerarqu&#x00ed;a y tabla por
subclase usando este enfoque:
</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>
Para cualquiera de estas estrategias de mapeo, una asociaci&#x00f3;n polim&#x00f3;rfica
a la clase ra&#x00ed;z <literal>Payment</literal> es mapeada usando <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="1">
<title>Tabla por clase concreta</title>
<para>
Podr&#x00ed;amos ir de dos maneras a la estrategia de mapeo de tabla por clase
concreta. La primera es usar <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>
Est&#x00e1;n implicadas tres tablas. Cada tabla define columnas para todas las
propiedades de la clase, inccluyendo las propiedades heredadas.
</para>
<para>
La limitaci&#x00f3;n de este enfoque es que si una propiedad es mapeada en la
superclase, el nombre de columna debe ser el mismo en todas las tablas
de subclase. (Podr&#x00ed;amos relajar esto en un lanzamiento futuro de Hibernate.)
La estrategia de generador de indentidad no est&#x00e1; permitida en la herencia
de uni&#x00f3;n de subclase, de hecho la semilla de clave primaria tiene que ser
compartida a trav&#x00e9;s de todas las subclases unidas de una jerarqu&#x00ed;a.
</para>
</sect2>
<sect2 id="inheritance-tableperconcreate-polymorphism">
<title>Tabla por clase concreta, usando polimorfismo impl&#x00ed;cito</title>
<para>
Un enfoque alternativo es hacer uso de polimorfismo impl&#x00ed;cito:
</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>
Nota que en ning&#x00fa;n sitio mencionamos la interface <literal>Payment</literal>
expl&#x00ed;citamente. Nota adem&#x00e1;s que las propiedades de <literal>Payment</literal>
son mapeadas en cada una de las subclases. Si quieres evitar duplicaci&#x00f3;n,
considera usar entidades XML. (por ejemplo,
<literal>[ &lt;!ENTITY allproperties SYSTEM "allproperties.xml"&gt; ]</literal>
en la declaraci&#x00f3;n <literal>DOCTYPE</literal> y <literal>&amp;allproperties;</literal>
en el mapeo).
</para>
<para>
La desventaja de este enfoque es que Hibernate no genera <literal>UNION</literal>s
de SQL al realizar consultas polim&#x00f3;rficas.
</para>
<para>
Para esta estrategia de mapeo, una asociaci&#x00f3;n polim&#x00f3;rfica a <literal>Payment</literal>
es mapeada generalmente usando <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>Mezclando polimorfismo impl&#x00ed;cito con otros mapeos de herencia</title>
<para>
Hay una cosa m&#x00e1;s por notar acerca de este mapeo. Ya que las subclases se mapean
cada una en su propio elemento <literal>&lt;class&gt;</literal> (y ya que
<literal>Payment</literal> es s&#x00f3;lo una interface), cada una de las subclases
podr&#x00ed;a ser parte de otra jerarqu&#x00ed;a de herencia! (Y todav&#x00ed;a puedes seguir usando
consultas polim&#x00f3;rficas contra la 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>
Una vez m&#x00e1;s, no mencionamos a <literal>Payment</literal> expl&#x00ed;citamente.
Si ejecutamos una consulta contra la interface <literal>Payment</literal>
- por ejemplo, <literal>from Payment</literal> - Hibernate devuelve
autom&#x00e1;ticamente instancias de <literal>CreditCardPayment</literal>
(y sus subclases, ya que ellas tambi&#x00e9;n implementan <literal>Payment</literal>),
<literal>CashPayment</literal> y <literal>ChequePayment</literal> pero
no instancias de <literal>NonelectronicTransaction</literal>.
</para>
</sect2>
</sect1>
<sect1 id="inheritance-limitations">
<title>Limitaciones</title>
<para>
Existen ciertas limitaciones al enfoque de "polimorfismo impl&#x00ed;cito" en
la estrategia de mapeo de tabla por clase concreta. Existen limitaciones
algo menos restrictivas a los mapeos <literal>&lt;union-subclass&gt;</literal>.
</para>
<para>
La siguiente tabla muestra las limitaciones de mapeos de tabla por
clase concreta, y de polmorfismo impl&#x00ed;cito, en Hibernate.
</para>
<table frame="topbot">
<title>Funcionalidades de mapeo de herencia</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>Estrategia de herencia</entry>
<entry>muchos-a-uno polim&#x00f3;rfica</entry>
<entry>uno-a-uno polim&#x00f3;rfica</entry>
<entry>uno-a-muchos polim&#x00f3;rfica</entry>
<entry>mushos-a-muchos polim&#x00f3;rfica</entry>
<entry><literal>load()/get()</literal> polim&#x00f3;rficos</entry>
<entry>Consultas polim&#x00f3;rficas</entry>
<entry>Uniones polim&#x00f3;rficas</entry>
<entry>Recuperaci&#x00f3;n por uni&#x00f3;n externa (outer join)</entry>
</row>
</thead>
<tbody>
<row>
<entry>tabla por jerarqu&#x00ed;a de clases</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>soportada</emphasis></entry>
</row>
<row>
<entry>tabla por subclase</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>soportada</emphasis></entry>
</row>
<row>
<entry>tabla por clase concreta (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> (para <literal>inverse="true"</literal> solamente)</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>soportada</emphasis></entry>
</row>
<row>
<entry>tabla por clase concreta (polimorfismo impl&#x00ed;cito)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>no soportada</emphasis></entry>
<entry><emphasis>no soportada</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>no suportadas</emphasis></entry>
<entry><emphasis>no soportada</emphasis></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
</chapter>