Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 10. Mapeo de herencias

## 10.1. Las tres estrategias

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

- tabla por jerarquía de clases

- table per subclass

- tabla por clase concreta

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

- polimorfismo implícito

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

    >

### 10.1.1. Tabla por jerarquía de clases

Suponga que tenemos una interfaz `Payment`, con los implementadores `CreditCardPayment`, `CashPayment`, `ChequePayment`. El mapeo de tabla por jerarquía se vería 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">

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

            ...

        </subclass>

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

            ...

        </subclass>

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

            ...

        </subclass>

    </class

    >

Se requiere exactamente una tabla. Hay una limitación de esta estrategia de mapeo: las columnas declaradas por las subclases tal como `CCTYPE`, no pueden tener restricciones `NOT NULL`.

### 10.1.2. Tabla por subclase

Un mapeo de tabla por subclase se vería así:

    <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

    >

Se necesitan cuatro tablas. Las tres tablas de subclase tienen asociaciones de clave principal a la tabla de superclase de modo que en el modelo relacional es realmente una asociación uno-a-uno.

### 10.1.3. Tabla por subclase: utilizando un discriminador

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.

### 10.1.4. Mezcla de tabla por jerarquía de clases con tabla por subclase

Incluso puede mezclar las estrategias de tabla por jerarquía y tabla por subclase utilizando este enfoque:

    <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

    >

Para cualquiera de estas estrategias de mapeo, una asociación polimórfica a la clase raíz `Payment` es mapeada usando `<many-to-one>`.

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

### 10.1.5. Tabla por clase concreta

Hay dos maneras de mapear la tabla por estrategia de clase concreta. La primera es utilizar `<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

    >

Hay tres tablas involucradas. Cada tabla define columnas para todas las propiedades de la clase, incluyendo las propiedades heredadas.

La limitación de este enfoque es que si una propiedad se mapea en la superclase, el nombre de la columna debe ser el mismo en todas las tablas de subclase. La estrategia del generador de identidad no está permitida en la herencia de unión de subclase. La semilla de la clave principal tiene que compartirse a través de todas las subclases unidas de una jerarquía.

Si su superclase es abstracta, mapeéla con `abstract="true"`. Si no es abstracta, se necesita una tabla adicional (en el ejemplo anterior, por defecto es `PAYMENT`) para mantener las instancias de la superclase.

### 10.1.6. Tabla por clase concreta utilizando polimorfismo implícito

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 `UNION`es 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

    >

### 10.1.7. Mezcla de polimorfismo implícito con otros mapeos de herencia

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

## 10.2. Limitaciones

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.

Tabla 10.1. Funcionalidades de los mapeos de herencia

Estrategia de herencia | Polimórfico muchos-a-uno | Polimórfico uno-a-uno | Polimórfico uno-a-muchos | Polimórfico muchos-a-muchos | Polimórfico `load()/get()` | Consultas polimórficas | Uniones polimórficas | Recuperación por unión externa
--- | --- | --- | --- | --- | --- | --- | --- | ---
tabla por jerarquía de clases | `<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
tabla por clase concreta (union-subclass) | `<many-to-one>` | `<one-to-one>` | `<one-to-many>` (solo para `inverse="true"`) | `<many-to-many>` | `s.get(Payment.class, id)` | `from Payment p` | `from Order o join o.payment p` | supported
tabla por clase concreta (polimorfismo implícito) | `<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.

- AnteriorCapítulo 9. Mapeo de componentes
- Subir
- Inicio
- SiguienteCapítulo 11. Trabajo con objetos