Hibernate.orgCommunity Documentation
Hibernate soporta las tres estrategias básicas de mapeo de herencia:
tabla por jerarquía de clases
tabla por subclase
tabla por clase concreta
En adición, Hibernate soporta un cuarto, ligeramente diferente tipo de polimorfismo:
polimorfismo implícito
It is possible to use different mapping strategies for different branches of the same inheritance hierarchy. You can then make use of implicit polymorphism to achieve polymorphism across the whole hierarchy. However, Hibernate does not support mixing <subclass>
, <joined-subclass>
and <union-subclass>
mappings under the same root <class>
element. It is possible to mix together the table per hierarchy and table per subclass strategies under the the same <class>
element, by combining the <subclass>
and <join>
elements (see below for an example).
It is possible to define subclass
, union-subclass
, and joined-subclass
mappings in separate mapping documents directly beneath hibernate-mapping
. This allows you to extend a class hierarchy by adding a new mapping file. You must specify an extends
attribute in the subclass mapping, naming a previously mapped superclass. Previously this feature made the ordering of the mapping documents important. Since Hibernate3, the ordering of mapping files is irrelevant when using the extends keyword. The ordering inside a single mapping file still needs to be defined as superclasses before subclasses.
Es posible usar estrategias de mapeo diferentes para diferentes ramificaciones de la misma jerarquía de herencia, y entonces usar polimorfismo implícito para conseguir polimorfismo a través de toda la jerarquía. Sin embargo, Hibernate no soporta la mezcla de mapeos<subclass>
, y<joined-subclass>
y<union-subclass>
bajo el mismo elemento<class>
raíz. Es posible mezclar juntas las estrategias de tabla por jerarquía y tabla por subclase, bajo el mismo elemento<class>
, combinando los elementos<subclass>
y<join>
(ver debajo).
Suppose we have an interface Payment
with the implementors CreditCardPayment
, CashPayment
, and ChequePayment
. The table per hierarchy mapping would display in the following way:
<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 >
Exactly one table is required. There is a limitation of this mapping strategy: columns declared by the subclasses, such as CCTYPE
, cannot have NOT NULL
constraints.
A table per subclass mapping looks like this:
<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 >
Four tables are required. The three subclass tables have primary key associations to the superclass table so the relational model is actually a one-to-one association.
Hibernate's implementation of table per subclass does not require a discriminator column. Other object/relational mappers use a different implementation of table per subclass that requires a type discriminator column in the superclass table. The approach taken by Hibernate is much more difficult to implement, but arguably more correct from a relational point of view. If you want to use a discriminator column with the table per subclass strategy, you can combine the use of <subclass>
and <join>
, as follows:
<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"
dice a Hibernate que no recupere los datos de la subclase ChequePayment
usando una unión externa (outer join) al consultar la superclase.
You can even mix the table per hierarchy and table per subclass strategies using the following approach:
<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"/>
There are two ways we can map the table per concrete class strategy. First, you can use <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 >
Están implicadas tres tablas. Cada tabla define columnas para todas las propiedades de la clase, inccluyendo las propiedades heredadas.
The limitation of this approach is that if a property is mapped on the superclass, the column name must be the same on all subclass tables. The identity generator strategy is not allowed in union subclass inheritance. The primary key seed has to be shared across all unioned subclasses of a hierarchy.
If your superclass is abstract, map it with abstract="true"
. If it is not abstract, an additional table (it defaults to PAYMENT
in the example above), is needed to hold instances of the superclass.
Un enfoque alternativo es hacer uso de 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 >
Notice that the Payment
interface is not mentioned explicitly. Also notice that properties of Payment
are mapped in each of the subclasses. If you want to avoid duplication, consider using XML entities (for example, [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]
in the DOCTYPE
declaration and &allproperties;
in the mapping).
La desventaja de este enfoque es que Hibernate no genera UNION
s de SQL al realizar consultas polimórficas.
Para esta estrategia de mapeo, una asociación polimórfica a Payment
es mapeada generalmente usando <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 >
Since the subclasses are each mapped in their own <class>
element, and since Payment
is just an interface), each of the subclasses could easily be part of another inheritance hierarchy. You can still use polymorphic queries against the Payment
interface.
<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 >
Once again, Payment
is not mentioned explicitly. If we execute a query against the Payment
interface, for example from Payment
, Hibernate automatically returns instances of CreditCardPayment
(and its subclasses, since they also implement Payment
), CashPayment
and ChequePayment
, but not instances of NonelectronicTransaction
.
There are limitations to the "implicit polymorphism" approach to the table per concrete-class mapping strategy. There are somewhat less restrictive limitations to <union-subclass>
mappings.
La siguiente tabla muestra las limitaciones de mapeos de tabla por clase concreta, y de polmorfismo implícito, en Hibernate.
Tabla 9.1. Funcionalidades de mapeo de herencia
Estrategia de herencia | muchos-a-uno polimórfica | uno-a-uno polimórfica | uno-a-muchos polimórfica | mushos-a-muchos polimórfica | load()/get() polimórficos | Consultas polimórficas | Uniones polimórficas | Recuperación por unión externa (outer join) |
---|---|---|---|---|---|---|---|---|
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 | soportada |
tabla por subclase | <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 | soportada |
tabla por clase concreta (union-subclass) | <many-to-one> | <one-to-one> | <one-to-many> (para inverse="true" solamente) | <many-to-many> | s.get(Payment.class, id) | from Payment p | from Order o join o.payment p | soportada |
tabla por clase concreta (polimorfismo implícito) | <any> | no soportada | no soportada | <many-to-any> | s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult() | from Payment p | no soportada | no soportada |
Copyright © 2004 Red Hat Middleware, LLC.