Mapeo de Herencia
Las Tres Estrategias
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
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).
Tabla por jerarquía de clases
Supón que tenemos una interface Payment, con
los implementadores CreditCardPayment,
CashPayment, ChequePayment.
El mapeo de tabla por jerarquía se vería así:
...
...
...
...
]]>
Se requiere exactamente una tabla. Hay una gran limitación de esta estrategia de mapeo:
las columnas declaradas por las subclases, como CCTYPE, no pueden
tener restricciones NOT NULL.
Tabla por subclase
Un mapeo de tabla por sublclase se vería así:
...
...
...
...
]]>
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ón uno-a-uno).
Tabla por subclase, usando un discriminador
Observa que 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 requiere 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 quisieras usar una columna discriminadora con la estrategia de
tabla por subclase, puedes combinar el uso de <subclass>
y <join>, como sigue:
...
...
...
...
]]>
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.
Mezclando tabla por jerarquía de clases con tabla por subclase
Puedes incluso mezclar las estrategias de tabla po jerarquía y tabla por
subclase usando este enfoque:
...
...
...
...
]]>
Para cualquiera de estas estrategias de mapeo, una asociación polimórfica
a la clase raíz Payment es mapeada usando <many-to-one>.
]]>
Tabla por clase concreta
Podríamos ir de dos maneras a la estrategia de mapeo de tabla por clase
concreta. La primera es usar <union-subclass>.
...
...
...
...
]]>
Están implicadas tres tablas. Cada tabla define columnas para todas las
propiedades de la clase, inccluyendo las propiedades heredadas.
La limitació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íamos relajar esto en un lanzamiento futuro de Hibernate.)
La estrategia de generador de indentidad no está permitida en la herencia
de unión de subclase, de hecho la semilla de clave primaria tiene que ser
compartida a través de todas las subclases unidas de una jerarquía.
Tabla por clase concreta, usando polimorfismo implícito
Un enfoque alternativo es hacer uso de polimorfismo implícito:
...
...
...
]]>
Nota que en ningún sitio mencionamos la interface Payment
explícitamente. Nota además que las propiedades de Payment
son mapeadas en cada una de las subclases. Si quieres evitar duplicación,
considera 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 UNIONs
de SQL al realizar consultas polimórficas.
Para esta estrategia de mapeo, una asociación polimórfica a Payment
es mapeada generalmente usando <any>.
]]>
Mezclando polimorfismo implícito con otros mapeos de herencia
Hay una cosa más por notar acerca de este mapeo. Ya que las subclases se mapean
cada una en su propio elemento <class> (y ya que
Payment es sólo una interface), cada una de las subclases
podría ser parte de otra jerarquía de herencia! (Y todavía puedes seguir usando
consultas polimórficas contra la interface Payment.)
...
...
...
...
]]>
Una vez más, no mencionamos a Payment explícitamente.
Si ejecutamos una consulta contra la interface Payment
- por ejemplo, from Payment - Hibernate devuelve
automáticamente instancias de CreditCardPayment
(y sus subclases, ya que ellas también implementan Payment),
CashPayment y ChequePayment pero
no instancias de NonelectronicTransaction.
Limitaciones
Existen ciertas limitaciones al enfoque de "polimorfismo implícito" en
la estrategia de mapeo de tabla por clase concreta. Existen limitaciones
algo menos restrictivas a los mapeos <union-subclass>.
La siguiente tabla muestra las limitaciones de mapeos de tabla por
clase concreta, y de polmorfismo implícito, en Hibernate.
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 suportadas
no soportada