Mapeo de Componentes
La noción de un componente es reusada en muchos contextos diferentes,
para propósitos diferentes, a través de Hibernate.
Objetos dependientes
Un componente es un objeto contenido que es persistido como un tipo de valor, no una
referencia de entidad. El término "componente" hace referencia a la noción orientada a
objetos de composición (no a componentes a nivel de arquitectura). Por ejemplo, podrías
modelar una persona como:
Ahora Name puede ser persistido como un componente de
Person. Observa que Name define métodos
getter y setter para sus propiedades persistentes, pero no necesita declarar
ninguna interface ni propiedades identificadoras.
Nuestro mapeo de Hibernate se vería así:
]]>
La tabla person tendría las columnas pid,
birthday,
initial,
first y
last.
Como todos los tipos de valor, los componentes no soportan referencias compartidas.
En otras palabras, dos personas pueden tener el mismo nombre, pero los dos objetos
persona contendrían dos objetos nombre independientes, sólo "iguales" en valor.
La semántica de valor nulo de un componente es ad hoc.
Cuando se recargue el objeto contenedor, Hibernate asumirá que si todas las columnas del
componente son nulas, el componente entero es nulo. Esto debe estar bien para la mayoría
de propósitos.
Las propiedades de un componentes pueden ser de cualquier tipo de Hibernate
(colecciones, muchos-a-uno, asociaciones, otros componentes, etc). Los componentes
anidados no deben ser considerados un uso exótico. Hibernate está
concebido para soportar un modelo de objetos granularizado en fino.
El elemento <component> permite un subelemento
<parent> que mapee una propiedad de la clase del componente
como una referencia de regreso a la entidad contenedora.
]]>
Colecciones de objetos dependientes
Las colecciones de componentes están soportadas (por ejemplo,
un array de tipo Name). Declara tu colección
de componentes remplazando la etiqueta <element>
por una etiqueta <composite-element>.
]]>
Nota: si defines un Set de elementos compuestos, es muy
importante implementar equals() y hashCode()
correctamente.
Los elementos compuestos pueden contener componentes pero no colecciones.
Si tu elemento compuesto contiene a su vez componentes, usa la etiqueta
<nested-composite-element>. Este es un caso bastante
exótico - una colección de componentes que a su vez tienen componentes. A esta
altura debes estar preguntándote si una asociación uno-a-muchos es más
apropiada. Intenta remodelar el elemento compuesto como una entidad - pero
observa que aunque el modelo Java es el mismo, el modelo relacional y la
semántica de persistencia siguen siendo ligeramente diferentes.
Por favor observa que un mapeo de elemento compuesto no soporta
propiedades nulables si estás usando un <set>.
Hibernate tiene que usar cada columna para identificar un registro
al borrar objetos (no hay una columna clave primaria separada en la tabla del
elemento compuesto), lo que es imposible con valores nulos. Tienes que, o bien usar
sólo propiedades no nulas en un elemento compuesto o elegir un
<list>, <map>,
<bag> o <idbag>.
Un caso especial de un elemento compuesto es un elemento compuesto con un
elemento anidado <many-to-one>. Un mapeo como este
te permite mapear columnas extra de una tabla de asociación muchos-a-muchos
a la clase del elemento compuesto. La siguiente es una asociación muchos-a-muchos
de Order a Item donde
purchaseDate, price y
quantity son propiedades de la asociación:
....
]]>
Por supuesto, no puede haber una referencia a la compra del otro lado para la
navegación bidireccional de la asociación. Recuerda que los componentes son tipos de
valor no permiten referencias compartidas. Una sola Purchase puede
estar en el conjunto de una Order, pero no puede ser referenciada
por el Item al mismo tiempo.
Incluso son posibles las asociaciones ternarias (o cuaternarias, etc):
....
]]>
Los elementos compuestos pueden aparecer en consultas usando la misma
sintáxis que las asociaciones a otras entidades.
Componentes como índices de Map
El elemento <composite-map-key> te permite mapear
una clase componente como la clave de un Map. Asegúrate que
sobrescribes hashCode() y equals()
correctamente en la clase componente.
Componentes como identificadores compuestos
Puedes usar un componente como un identidicador de una clase entidad. Tu clase
componente debe satisfacer ciertos requerimientos:
Debe implementar java.io.Serializable.
Debe re-implementar equals() y
hashCode(), consistentemente con la
noción de base de datos de igualdad de clave compuesta.
Nota: en Hibernat3, el segundo requerimiento no es absolutamente un
requerimiento rígido de Hibernate. Pero de todas formas, házlo.
No puedes usar un IdentifierGenerator para generar claves
compuestas. La aplicación debe, en cambio, asignar sus propios identificadores.
Usa la etiqueta <composite-id> (con elementos
anidados <key-property>) en lugar de la usual
declaración <id>. Por ejemplo, la clase
OrderLine tiene una clave primaria que depende de
la clave primaria (compuesta) de Order.
....
]]>
Ahora, cualquier clave foránea que referencie la tabla de OrderLine
es también compuesta. Debes declarar esto en tus mapeos de otras clases. Una asociación
a OrderLine sería mapeado así:
]]>
(Nota que la etiqueta <column> es una alternativa al
atributo column en cualquier sitio.)
Una asociación muchos-a-muchos a OrderLine
también usa la clave foránea compuesta:
]]>
La colección de OrderLines en Order usaría:
]]>
(El elemento <one-to-many>, como es usual, no declara columnas.)
Si OrderLine posee una colección por sí misma, tiene también
una clave foránea compuesta.
....
....
...
]]>
Componentes dinámicos
Puedes incluso mapear una propiedad de tipo Map:
]]>
La semántica de un mapeo <dynamic-component> es ídentica
a la de <component>. La ventaja de este tipo de mapeos es
la habilidad para determinar las propiedades reales del bean en tiempo de despliegue,
sólo con editar el documento de mapeo. La manipulación del documento de mapeo en tiempo
de ejecución es también posible, usando un analizador DOM. Incluso mejor, puedes acceder
(y cambiar) el metamodelo de tiempo de configuración de Hibernate por medio del objeto
Configuration.