Hibernate.orgCommunity Documentation
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
>
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
.
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.
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.
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"/>
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.
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
>
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.
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.