hibernate-orm/reference/es/modules/component_mapping.xml

404 lines
16 KiB
XML
Raw Normal View History

<chapter id="components">
<title>Mapeo de Componentes</title>
<para>
La noci&#x00f3;n de un <emphasis>componente</emphasis> es reusada en muchos contextos diferentes,
para prop&#x00f3;sitos diferentes, a trav&#x00e9;s de Hibernate.
</para>
<sect1 id="components-dependentobjects">
<title>Objetos dependientes</title>
<para>
Un componente es un objeto contenido que es persistido como un tipo de valor, no una
referencia de entidad. El t&#x00e9;rmino "componente" hace referencia a la noci&#x00f3;n orientada a
objetos de composici&#x00f3;n (no a componentes a nivel de arquitectura). Por ejemplo, podr&#x00ed;as
modelar una persona como:
</para>
<programlisting><![CDATA[public class Person {
private java.util.Date birthday;
private Name name;
private String key;
public String getKey() {
return key;
}
private void setKey(String key) {
this.key=key;
}
public java.util.Date getBirthday() {
return birthday;
}
public void setBirthday(java.util.Date birthday) {
this.birthday = birthday;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}]]></programlisting>
<programlisting><![CDATA[public class Name {
char initial;
String first;
String last;
public String getFirst() {
return first;
}
void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
void setLast(String last) {
this.last = last;
}
public char getInitial() {
return initial;
}
void setInitial(char initial) {
this.initial = initial;
}
}]]></programlisting>
<para>
Ahora <literal>Name</literal> puede ser persistido como un componente de
<literal>Person</literal>. Observa que <literal>Name</literal> define m&#x00e9;todos
getter y setter para sus propiedades persistentes, pero no necesita declarar
ninguna interface ni propiedades identificadoras.
</para>
<para>
Nuestro mapeo de Hibernate se ver&#x00ed;a as&#x00ed;:
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
<para>
La tabla person tendr&#x00ed;a las columnas <literal>pid</literal>,
<literal>birthday</literal>,
<literal>initial</literal>,
<literal>first</literal> y
<literal>last</literal>.
</para>
<para>
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&#x00ed;an dos objetos nombre independientes, s&#x00f3;lo "iguales" en valor.
La sem&#x00e1;ntica de valor nulo de un componente es <emphasis>ad hoc</emphasis>.
Cuando se recargue el objeto contenedor, Hibernate asumir&#x00e1; que si todas las columnas del
componente son nulas, el componente entero es nulo. Esto debe estar bien para la mayor&#x00ed;a
de prop&#x00f3;sitos.
</para>
<para>
Las propiedades de un componentes pueden ser de cualquier tipo de Hibernate
(colecciones, muchos-a-uno, asociaciones, otros componentes, etc). Los componentes
anidados <emphasis>no</emphasis> deben ser considerados un uso ex&#x00f3;tico. Hibernate est&#x00e1;
concebido para soportar un modelo de objetos granularizado en fino.
</para>
<para>
El elemento <literal>&lt;component&gt;</literal> permite un subelemento
<literal>&lt;parent&gt;</literal> que mapee una propiedad de la clase del componente
como una referencia de regreso a la entidad contenedora.
</para>
<programlisting><![CDATA[<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid.hex"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- reference back to the Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class>]]></programlisting>
</sect1>
<sect1 id="components-incollections" revision="1">
<title>Colecciones de objetos dependientes</title>
<para>
Las colecciones de componentes est&#x00e1;n soportadas (por ejemplo,
un array de tipo <literal>Name</literal>). Declara tu colecci&#x00f3;n
de componentes remplazando la etiqueta <literal>&lt;element&gt;</literal>
por una etiqueta <literal>&lt;composite-element&gt;</literal>.
</para>
<programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
<key column="id"/>
<composite-element class="eg.Name"> <!-- class attribute required -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set>]]></programlisting>
<para>
Nota: si defines un <literal>Set</literal> de elementos compuestos, es muy
importante implementar <literal>equals()</literal> y <literal>hashCode()</literal>
correctamente.
</para>
<para>
Los elementos compuestos pueden contener componentes pero no colecciones.
Si tu elemento compuesto contiene a su vez componentes, usa la etiqueta
<literal>&lt;nested-composite-element&gt;</literal>. Este es un caso bastante
ex&#x00f3;tico - una colecci&#x00f3;n de componentes que a su vez tienen componentes. A esta
altura debes estar pregunt&#x00e1;ndote si una asociaci&#x00f3;n uno-a-muchos es m&#x00e1;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&#x00e1;ntica de persistencia siguen siendo ligeramente diferentes.
</para>
<para>
Por favor observa que un mapeo de elemento compuesto no soporta
propiedades nulables si est&#x00e1;s usando un <literal>&lt;set&gt;</literal>.
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&#x00f3;lo propiedades no nulas en un elemento compuesto o elegir un
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal> o <literal>&lt;idbag&gt;</literal>.
</para>
<para>
Un caso especial de un elemento compuesto es un elemento compuesto con un
elemento anidado <literal>&lt;many-to-one&gt;</literal>. Un mapeo como este
te permite mapear columnas extra de una tabla de asociaci&#x00f3;n muchos-a-muchos
a la clase del elemento compuesto. La siguiente es una asociaci&#x00f3;n muchos-a-muchos
de <literal>Order</literal> a <literal>Item</literal> donde
<literal>purchaseDate</literal>, <literal>price</literal> y
<literal>quantity</literal> son propiedades de la asociaci&#x00f3;n:
</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.Purchase">
<property name="purchaseDate"/>
<property name="price"/>
<property name="quantity"/>
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
</composite-element>
</set>
</class>]]></programlisting>
<para>
Por supuesto, no puede haber una referencia a la compra del otro lado para la
navegaci&#x00f3;n bidireccional de la asociaci&#x00f3;n. Recuerda que los componentes son tipos de
valor no permiten referencias compartidas. Una sola <literal>Purchase</literal> puede
estar en el conjunto de una <literal>Order</literal>, pero no puede ser referenciada
por el <literal>Item</literal> al mismo tiempo.
</para>
<para>Incluso son posibles las asociaciones ternarias (o cuaternarias, etc):</para>
<programlisting><![CDATA[<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.OrderLine">
<many-to-one name="purchaseDetails class="eg.Purchase"/>
<many-to-one name="item" class="eg.Item"/>
</composite-element>
</set>
</class>]]></programlisting>
<para>
Los elementos compuestos pueden aparecer en consultas usando la misma
sint&#x00e1;xis que las asociaciones a otras entidades.
</para>
</sect1>
<sect1 id="components-asmapindex">
<title>Componentes como &#x00ed;ndices de Map</title>
<para>
El elemento <literal>&lt;composite-map-key&gt;</literal> te permite mapear
una clase componente como la clave de un <literal>Map</literal>. Aseg&#x00fa;rate que
sobrescribes <literal>hashCode()</literal> y <literal>equals()</literal>
correctamente en la clase componente.
</para>
</sect1>
<sect1 id="components-compositeid" revision="1">
<title>Componentes como identificadores compuestos</title>
<para>
Puedes usar un componente como un identidicador de una clase entidad. Tu clase
componente debe satisfacer ciertos requerimientos:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Debe implementar <literal>java.io.Serializable</literal>.
</para>
</listitem>
<listitem>
<para>
Debe re-implementar <literal>equals()</literal> y
<literal>hashCode()</literal>, consistentemente con la
noci&#x00f3;n de base de datos de igualdad de clave compuesta.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>Nota: en Hibernat3, el segundo requerimiento no es absolutamente un
requerimiento r&#x00ed;gido de Hibernate. Pero de todas formas, h&#x00e1;zlo.</emphasis>
</para>
<para>
No puedes usar un <literal>IdentifierGenerator</literal> para generar claves
compuestas. La aplicaci&#x00f3;n debe, en cambio, asignar sus propios identificadores.
</para>
<para>
Usa la etiqueta <literal>&lt;composite-id&gt;</literal> (con elementos
anidados <literal>&lt;key-property&gt;</literal>) en lugar de la usual
declaraci&#x00f3;n <literal>&lt;id&gt;</literal>. Por ejemplo, la clase
<literal>OrderLine</literal> tiene una clave primaria que depende de
la clave primaria (compuesta) de <literal>Order</literal>.
</para>
<programlisting><![CDATA[<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class>]]></programlisting>
<para>
Ahora, cualquier clave for&#x00e1;nea que referencie la tabla de <literal>OrderLine</literal>
es tambi&#x00e9;n compuesta. Debes declarar esto en tus mapeos de otras clases. Una asociaci&#x00f3;n
a <literal>OrderLine</literal> ser&#x00ed;a mapeado as&#x00ed;:
</para>
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>]]></programlisting>
<para>
(Nota que la etiqueta <literal>&lt;column&gt;</literal> es una alternativa al
atributo <literal>column</literal> en cualquier sitio.)
</para>
<para>
Una asociaci&#x00f3;n <literal>muchos-a-muchos</literal> a <literal>OrderLine</literal>
tambi&#x00e9;n usa la clave for&#x00e1;nea compuesta:
</para>
<programlisting><![CDATA[<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set>]]></programlisting>
<para>
La colecci&#x00f3;n de <literal>OrderLine</literal>s en <literal>Order</literal> usar&#x00ed;a:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(El elemento <literal>&lt;one-to-many&gt;</literal>, como es usual, no declara columnas.)
</para>
<para>
Si <literal>OrderLine</literal> posee una colecci&#x00f3;n por s&#x00ed; misma, tiene tambi&#x00e9;n
una clave for&#x00e1;nea compuesta.
</para>
<programlisting><![CDATA[<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key> <!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>]]></programlisting>
</sect1>
<sect1 id="components-dynamic" revision="1">
<title>Componentes din&#x00e1;micos</title>
<para>
Puedes incluso mapear una propiedad de tipo <literal>Map</literal>:
</para>
<programlisting><![CDATA[<dynamic-component name="userAttributes">
<property name="foo" column="FOO"/>
<property name="bar" column="BAR"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>]]></programlisting>
<para>
La sem&#x00e1;ntica de un mapeo <literal>&lt;dynamic-component&gt;</literal> es &#x00ed;dentica
a la de <literal>&lt;component&gt;</literal>. La ventaja de este tipo de mapeos es
la habilidad para determinar las propiedades reales del bean en tiempo de despliegue,
s&#x00f3;lo con editar el documento de mapeo. La manipulaci&#x00f3;n del documento de mapeo en tiempo
de ejecuci&#x00f3;n es tambi&#x00e9;n posible, usando un analizador DOM. Incluso mejor, puedes acceder
(y cambiar) el metamodelo de tiempo de configuraci&#x00f3;n de Hibernate por medio del objeto
<literal>Configuration</literal>.
</para>
</sect1>
</chapter>