Hibernate.orgCommunity Documentation

Capítulo 10. Mapeo de herencias

10.1. Las tres estrategias
10.1.1. Tabla por jerarquía de clases
10.1.2. Tabla por subclase
10.1.3. Tabla por subclase: utilizando un discriminador
10.1.4. Mezcla de tabla por jerarquía de clases con tabla por subclase
10.1.5. Tabla por clase concreta
10.1.6. Tabla por clase concreta utilizando polimorfismo implícito
10.1.7. Mezcla de polimorfismo implícito con otros mapeos de herencia
10.2. Limitaciones

Hibernate soporta las tres estrategias básicas de mapeo de herencia:

Además, Hibernate soporta un cuarto, un tipo ligeramente diferente de polimorfismo:

Es posible utilizar estrategias de mapeo diferentes para diferentes ramificaciones de la misma jerarquía de herencia. Luego puede usar un polimorfismo implícito para conseguir polimorfismo a través de toda la jerarquía. Sin embargo, Hibernate no soporta la mezcla de mapeos <subclass>, <joined-subclass> y <union-subclass> bajo el mismo elemento <class> raíz. Es posible mezclar las estrategias de tabla por jerarquía y tabla por subclase bajo el mismo elemento <class>, combinando los elementos <subclass> y <join> (a continuación encontrará un ejemplo).

Es posible definir los mapeos subclass, union-subclass, y joined-subclass en documentos de mapeo separados, directamente debajo de hibernate-mapping. Esto le permite extender une jerarquía de clase sólamente añadiendo un nuevo archivo de mapeo. Tiene que especificar un atributo extends en la subclase de mapeo, nombrando una superclase mapeada previamente. Nota: Anteriormente esta característica hacia que el orden de los documentos de mapeo fuera importante. Desde Hibernate3, el orden de los archivos de mapeo no tiene relevancia cuando se utiliza la palabra clave extends. El orden dentro de un sólo archivo de mapeo todavía necesita ser definido como superclases antes de subclases.



 <hibernate-mapping>
     <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
          <property name="name" type="string"/>
     </subclass>
 </hibernate-mapping
>

La implementación de Hibernate de tabla por subclase no requiere ninguna columna discriminadora. Otros mapeadores objeto/relacional usan una implementación diferente de tabla por subclase que necesita una columna discriminadora de tipo en la tabla de superclase. Este enfoque es mucho más difícil de implementar pero discutiblemente más correcto desde un punto de vista relacional. Si quisiere utilizar una columna discriminadora con la estrategia de tabla por subclase, puede combinar el uso de <subclass> y <join>, así:


<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 declaración opcional fetch="select" le dice a Hibernate que no recupere los datos de la subclase ChequePayment utilizando una unión externa (outer join) al consultar la superclase.

Un enfoque alternativo es para hacer uso del polimorfismo implícito:


<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
>

Observe que la interfaz Payment no se menciona explícitamente. También note que las propiedades de Payment se mapean en cada una de las subclases. Si quiere evitar la duplicación, considere el usar entidades XML (por ejemplo, [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] en la declaración DOCTYPE y &allproperties; en el mapeo).

La desventaja de este enfoque es que Hibernate no genera UNIONes de SQL al realizar consultas polimórficas.

Para esta estrategia de mapeo, una asociación polimórfica a Payment es mapeada generalmente utilizando <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
>

Ya que las subclases se mapean cada una en su propio elemento <class> y debido a que Payment es sólo una interfaz, cada una de las subclases podría ser fácilmente parte de otra jerarquía de herencia. Todavía puede seguir usando consultas polimórficas contra la interfaz 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
>

Una vez más, no mencionamos a Payment explícitamente. Si ejecutamos una consulta frente a la interfaz Payment - por ejemplo, from Payment, Hibernate retorna automáticamente instancias de CreditCardPayment (y sus subclases, ya que ellas también implementan Payment), CashPayment y ChequePayment pero no las instancias de NonelectronicTransaction.

Existen ciertas limitaciones al enfoque de "polimorfismo implícito" en la estrategia de mapeo de tabla por clase concreta. Existen limitaciones un poco menos restrictivas a los mapeos <union-subclass>.

La siguiente tabla muestra las limitaciones de los mapeos de tabla por clase concreta y del polimorfismo implícito en Hibernate.