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

1242 lines
51 KiB
XML

<chapter id="collections">
<title>Mapeo de Colecciones</title>
<sect1 id="collections-persistent" revision="3">
<title>Colecciones persistentes</title>
<para>
Hibernate requiere que los campos valuados en colecci&#x00f3;n
persistentes sean declarados como un tipo de interface, por ejemplo:
</para>
<programlisting><![CDATA[public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}]]></programlisting>
<para>
La interface real podr&#x00ed;a ser <literal>java.util.Set</literal>,
<literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
<literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
<literal>java.util.SortedMap</literal> o ... lo que te guste!
(Donde "lo que te guste" significa que tendr&#x00e1;s que escribir una
implementaci&#x00f3;n de <literal>org.hibernate.usertype.UserCollectionType</literal>.)
</para>
<para>
Nota c&#x00f3;mo hemos inicializado la variable de instancia de
<literal>HashSet</literal>. Esta es la mejor forma de inicializar
propiedades valuadas en colecci&#x00f3;n de instancias reci&#x00e9;n
instanciadas (no persistentes). Cuando haces persistente la instancia
- llamando a <literal>persist()</literal>, por ejemplo - Hibernate realmente
remplazar&#x00e1; el <literal>HashSet</literal> con una instancia de una
implementaci&#x00f3;n de <literal>Set</literal> propia de Hibernate.
Observa errores como este:
</para>
<programlisting><![CDATA[Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
kittens = cat.getKittens(); // Okay, kittens collection is a Set
(HashSet) cat.getKittens(); // Error!]]></programlisting>
<para>
Las colecciones persistentes inyectadas por Hibernate se comportan
como <literal>HashMap</literal>, <literal>HashSet</literal>,
<literal>TreeMap</literal>, <literal>TreeSet</literal> o
<literal>ArrayList</literal>, dependiendo del tipo de interface.
</para>
<para>
Las instancias de colecciones tienen el comportamiento usual de tipos de valor.
Son autom&#x00e1;ticamente persistidas al ser referenciadas por un objeto
persistente y autom&#x00e1;ticamente borradas al desreferenciarse. Si una
colecci&#x00f3;n es pasada de un objeto persistente a otro, sus elementos
ser&#x00ed;an movidos de una tabla a otra. Dos entidades pueden no
compartir una referencia a la misma instancia de colecci&#x00f3;n.
Debido al modelo relacional subyacente, las propiedades valuadas en
colecci&#x00f3;n no soportan la sem&#x00e1;ntica de valor nulo. Hibernate no
distingue entre una referencia de colecci&#x00f3;n nula y una colecci&#x00f3;n
vac&#x00ed;a.
</para>
<para>
No debes tener que preocuparte demasiado por esto. Usa las colecciones
persistentes de la misma forma en que usas colecciones de Java ordinarias.
S&#x00f3;lo aseg&#x00fa;rate que entiendes la sem&#x00e1;ntica de las asociaciones
bidireccionales (discutida luego).
</para>
</sect1>
<sect1 id="collections-mapping" revision="2">
<title>Mapeos de colecci&#x00f3;n</title>
<para>
El elemento de mapeo de Hibernate usado para mapear una colecci&#x00f3;n
depende del tipo de la interface. Por ejemplom un elemento
<literal>&lt;set&gt;</literal> se usa para mapear propiedades de tipo
<literal>Set</literal>.
</para>
<programlisting><![CDATA[<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class>]]></programlisting>
<para>
Aparte de <literal>&lt;set&gt;</literal>, existen adem&#x00e1;s
los elementos de mapeo <literal>&lt;list&gt;</literal>,
<literal>&lt;map&gt;</literal>, <literal>&lt;bag&gt;</literal>,
<literal>&lt;array&gt;</literal> y <literal>&lt;primitive-array&gt;</literal>.
El elemento <literal>&lt;map&gt;</literal> es representativo:
</para>
<programlistingco>
<areaspec>
<area id="mappingcollection1" coords="2 65"/>
<area id="mappingcollection2" coords="3 65"/>
<area id="mappingcollection3" coords="4 65"/>
<area id="mappingcollection4" coords="5 65"/>
<area id="mappingcollection5" coords="6 65"/>
<area id="mappingcollection6" coords="7 65"/>
<area id="mappingcollection7" coords="8 65"/>
<area id="mappingcollection8" coords="9 65"/>
<area id="mappingcollection9" coords="10 65"/>
<area id="mappingcollection10" coords="11 65"/>
<area id="mappingcollection11" coords="12 65"/>
<area id="mappingcollection12" coords="13 65"/>
<area id="mappingcollection13" coords="14 65"/>
</areaspec>
<programlisting><![CDATA[<map
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
where="arbitrary sql where condition"
fetch="join|select|subselect"
batch-size="N"
access="field|property|ClassName"
optimistic-lock="true|false"
node="element-name|."
embed-xml="true|false"
>
<key .... />
<map-key .... />
<element .... />
</map>]]></programlisting>
<calloutlist>
<callout arearefs="mappingcollection1">
<para>
<literal>name</literal> el nombre de la propiedad de colecci&#x00f3;n
</para>
</callout>
<callout arearefs="mappingcollection2">
<para>
<literal>table</literal> (opcional - por defecto al nombre de la propiedad)
el nombre de la tabla de colecii&#x00f3;n (no usado para asociaciones
uno-a-muchos)
</para>
</callout>
<callout arearefs="mappingcollection3">
<para>
<literal>schema</literal> (opcional) el nombre de un esquema de tablas
para sobrescribir el esquema declarado en el elemento ra&#x00ed;z
</para>
</callout>
<callout arearefs="mappingcollection4">
<para>
<literal>lazy</literal> (opcional - por defecto a <literal>true</literal>)
puede ser usado para deshabilitar la recuperaci&#x00f3;n perezosa y
especificar que la asociaci&#x00f3;n es siempre recuperada tempranamente
(no disponible para arrays)
</para>
</callout>
<callout arearefs="mappingcollection5">
<para>
<literal>inverse</literal> (opcional - por defecto a <literal>false</literal>)
marca esta colecci&#x00f3;n como el extremo "inverso" de una asociaci&#x00f3;n
bidireccional.
</para>
</callout>
<callout arearefs="mappingcollection6">
<para>
<literal>cascade</literal> (opcional - por defecto a <literal>none</literal>)
habilita operaciones en cascada a entidades hijas
</para>
</callout>
<callout arearefs="mappingcollection7">
<para>
<literal>sort</literal> (opcional) especifica una colecci&#x00f3;n
con ordenamiento <literal>natural</literal>, o una clase comparadora
dada
</para>
</callout>
<callout arearefs="mappingcollection8">
<para>
<literal>order-by</literal> (opcional, s&#x00f3;lo JDK1.4) especifica una columna
de tabla (o columnas) que definen el orden de iteraci&#x00f3;n del
<literal>Map</literal>, <literal>Set</literal> o bag, junto a un
<literal>asc</literal> o <literal>desc</literal> opcional.
</para>
</callout>
<callout arearefs="mappingcollection9">
<para>
<literal>where</literal> (opcional) especifica una condici&#x00f3;n
<literal>WHERE</literal> de SQL arbitrario para ser usada al recuperar o
quitar la colecci&#x00f3;n (&#x00fa;til si la colecci&#x00f3;n debe
contener s&#x00f3;lo un subconjunto de los datos disponibles)
</para>
</callout>
<callout arearefs="mappingcollection10">
<para>
<literal>fetch</literal> (opcional, por defecto a <literal>select</literal>)
Elige entre recuperaci&#x00f3;n por uni&#x00f3;n externa (outer-join),
recuperar por selecci&#x00f3;n secuencial, y recuperaci&#x00f3;n por
subselecci&#x00f3;n secuencial.
</para>
</callout>
<callout arearefs="mappingcollection11">
<para>
<literal>batch-size</literal> (opcional, por defecto a <literal>1</literal>)
especifica un "tama&#x00f1;o de lote" para la recuperar
perezosamente instancias de esta colecci&#x00f3;n.
</para>
</callout>
<callout arearefs="mappingcollection12">
<para>
<literal>access</literal> (opcional - por defecto a <literal>property</literal>):
La estrategia que debe usar Hibernate para acceder al valor de la
propiedad.
</para>
</callout>
<callout arearefs="mappingcollection12">
<para>
<literal>optimistic-lock</literal> (opcional - por defecto a <literal>true</literal>):
Especifica que los cambios de estado de la colecci&#x00f3;n resultan en
incrementos de versi&#x00f3;n de la entidad due&#x00f1;a. (Para asociaciones
uno a muchos, frecuentemente es razonable deshabilitar esta opci&#x00f3;n.)
</para>
</callout>
</calloutlist>
</programlistingco>
<sect2 id="collections-foreignkeys" >
<title>Claves for&#x00e1;neas de collecci&#x00f3;n</title>
<para>
Las instancias de colecci&#x00f3;n se distinguen en la base de datos por la
clave for&#x00e1;nea de la entidad que posee la colecci&#x00f3;n. Se hace
referencia a esta clave for&#x00e1;nea como la <emphasis>columna clave de
colecci&#x00f3;n</emphasis> (o columnas) de la tabla de colecci&#x00f3;n.
La columna clave de la colecci&#x00f3;n es mapeada por el elemento
<literal>&lt;key&gt;</literal>.
</para>
<para>
Puede haber una restricci&#x00f3;n de nulabilidad sobre la columna de clave
for&#x00e1;nea. Para la mayor&#x00ed;a de colecciones, esto est&#x00e1; implicado.
Para asociaciones unidireccionales uno a muchos, la columna de clave
for&#x00e1;nea es nulable por defecto, de modo que podr&#x00ed;as necesitar
especificar <literal>not-null="true"</literal>.
</para>
<programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
<para>
La restricci&#x00f3;n de clave for&#x00e1;nea puede usar
<literal>ON DELETE CASCADE</literal>.
</para>
<programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
<para>
Mira el cap&#x00ed;tulo anterior por una definici&#x00f3;n completa del
elemento <literal>&lt;key&gt;</literal>.
</para>
</sect2>
<sect2 id="collections-elements" >
<title>Elementos de collecci&#x00f3;n</title>
<para>
Las colecciones pueden contener casi cualquier tipo de Hibernate, incluyendo
todos los tipos b&#x00e1;sicos, componentes, y por supuesto, referencias a otras
entidades. Esta es una distinci&#x00f3;n importante: un objeto en una colecci&#x00f3;n
puede ser manejado con una sem&#x00e1;ntica de "valor" (su ciclo de vida depende
completamente del propietario de la colecci&#x00f3;n) o podr&#x00ed;a ser una referencia
a otra entidad, con su propio ciclo de vida. En el &#x00fa;ltimo caso, s&#x00f3;lo
el estado del "enlace" entre los dos objetos se considera mantenido por la
colecci&#x00f3;n.
</para>
<para>
Se hace referencia al tipo contenido como el <emphasis>tipo de elemento
de la colecci&#x00f3;n</emphasis>. Los elementos de colecci&#x00f3;n son
mapeados por <literal>&lt;element&gt;</literal> o
<literal>&lt;composite-element&gt;</literal>, o en el caso de referencias
de entidades, con <literal>&lt;one-to-many&gt;</literal> o
<literal>&lt;many-to-many&gt;</literal>. Las dos primeras mapean elementos
con sem&#x00e1;ntica de valor, los dos siguientes son usados para mapear
asociaciones de entidades.
</para>
</sect2>
<sect2 id="collections-indexed">
<title>Colecciones indexadas</title>
<para>
Todos los mapeos de colecci&#x00f3;n, excepto aquellos con sem&#x00e1;ntica de
set o bag, necesitan una <emphasis>columna &#x00ed;ndice</emphasis> en la tabla
de colecci&#x00f3;n, una columna que mapea a un &#x00ed;ndice de array, o
&#x00ed;ndice de <literal>List</literal>, o clave de <literal>Map</literal>.
El &#x00ed;ndice de un <literal>Map</literal> puede ser de cualquier tipo
b&#x00e1;sico, mapeado con <literal>&lt;map-key&gt;</literal>, o puede ser
una referencia de entidad, mapeada con <literal>&lt;map-key-many-to-many&gt;</literal>,
o puede ser un tipo compuesto, mapeado con <literal>&lt;composite-map-key&gt;</literal>.
El &#x00ed;ndice de un array o lista es siempre de tipo <literal>integer</literal>
y se mapea usando el elemento <literal>&lt;list-index&gt;</literal>. La columna
mapeada contiene enteros secuenciales (numerados desde cero, por defecto).
</para>
<programlistingco>
<areaspec>
<area id="index1" coords="2 45"/>
<area id="index2" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<list-index
column="column_name"
base="0|1|..."/>]]></programlisting>
<calloutlist>
<callout arearefs="index1">
<para>
<literal>column_name</literal> (requerido): El nombre de la columna que tiene
los valores &#x00ed;ndice de la colecci&#x00f3;n.
</para>
</callout>
<callout arearefs="index1">
<para>
<literal>base</literal> (opcional, por defecto a <literal>0</literal>): El valor
de la columna &#x00ed;ndice que corresponde al primer elemento de la lista o
array.
</para>
</callout>
</calloutlist>
</programlistingco>
<programlistingco>
<areaspec>
<area id="mapkey1" coords="2 45"/>
<area id="mapkey2" coords="3 45"/>
<area id="mapkey3" coords="4 45"/>
</areaspec>
<programlisting><![CDATA[<map-key
column="column_name"
formula="any SQL expression"
type="type_name"
node="@attribute-name"
length="N"/>]]></programlisting>
<calloutlist>
<callout arearefs="mapkey1">
<para>
<literal>column</literal> (opcional): El nombre de la columna que tiene
los valores &#x00ed;ndice de la colecci&#x00f3;n.
</para>
</callout>
<callout arearefs="mapkey2">
<para>
<literal>formula</literal> (opcional): Una f&#x00f3;rmula SQL usada para
evaluar la clave del mapa.
</para>
</callout>
<callout arearefs="mapkey3">
<para>
<literal>type</literal> (requerido): el tipo de las claves del mapa.
</para>
</callout>
</calloutlist>
</programlistingco>
<programlistingco>
<areaspec>
<area id="indexmanytomany1" coords="2 45"/>
<area id="indexmanytomany2" coords="3 45"/>
<area id="indexmanytomany3" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<map-key-many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
/>]]></programlisting>
<calloutlist>
<callout arearefs="indexmanytomany1">
<para>
<literal>column</literal> (opcional): El nombre de la columna clave
for&#x00e1;nea para los valores &#x00ed;ndice de la colecci&#x00f3;n.
</para>
</callout>
<callout arearefs="indexmanytomany2">
<para>
<literal>formula</literal> (opcional): Una f&#x00f3;rmula SQL usada para
evaluar la clave for&#x00e1;nea de la clave del mapa.
</para>
</callout>
<callout arearefs="indexmanytomany3">
<para>
<literal>class</literal> (requerido): La clase de entidad usada como clave del mapa.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Si tu tabla no tiene una columna &#x00ed;ndice, y deseas a&#x00fa;n usar <literal>List</literal> como
tipo de propiedad, debes mapear la propiedad como un <emphasis>&lt;bag&gt;</emphasis>
de Hibernate. Un bag (bolsa) no retiene su orden al ser recuperado de la base de datos,
pero puede ser ordenado o clasificado opcionalmente.
</para>
</sect2>
<para>
Hay absolutamente un rango de mapeos que pueden ser generados para colecciones,
cubriendo muchos modelos relacionales comunes. Te sugerimos que experimentes con
la herramienta de generaci&#x00f3;n de esquemas para obtener una idea de c&#x00f3;mo varias
declaraciones de mapeo se traducen a tablas de base de datos.
</para>
<sect2 id="collections-ofvalues" revision="1">
<title>Colecciones de valores y asociaciones muchos-a-muchos</title>
<para>
Cualquier colecci&#x00f3;n de valores o asociaci&#x00f3;n muchos a muchos requiere una
<emphasis>tabla de colecci&#x00f3;n</emphasis> dedicada con una columna o columnas
de clave for&#x00e1;nea, <emphasis>columna de elemento de colecci&#x00f3;n</emphasis> o
columnas y posiblemente una columna o columnas &#x00ed;ndice.
</para>
<para>
Para una colecci&#x00f3;n de valores, usamos la etiqueta <literal>&lt;element&gt;</literal>.
</para>
<programlistingco>
<areaspec>
<area id="element1b" coords="2 50"/>
<area id="element2b" coords="3 50"/>
<area id="element3b" coords="4 50"/>
</areaspec>
<programlisting><![CDATA[<element
column="column_name"
formula="any SQL expression"
type="typename"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>]]></programlisting>
<calloutlist>
<callout arearefs="element1b">
<para>
<literal>column</literal> (opcional): El nombre de la columna que tiene
los valores de los elementos de la colecci&#x00f3;n.
</para>
</callout>
<callout arearefs="element2b">
<para>
<literal>formula</literal> (opcional): Una f&#x00f3;rmula SQL usada para evaluar
el elemento.
</para>
</callout>
<callout arearefs="element3b">
<para>
<literal>type</literal> (requerido): El tipo del elemento de colecci&#x00f3;n.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Una <emphasis>asociaci&#x00f3;n muchos-a-muchos</emphasis> se especifica usando
el elemento <literal>&lt;many-to-many&gt;</literal>.
</para>
<programlistingco>
<areaspec>
<area id="manytomany1" coords="2 60"/>
<area id="manytomany2" coords="3 60"/>
<area id="manytomany3" coords="4 60"/>
<area id="manytomany4" coords="5 60"/>
<area id="manytomany5" coords="6 60"/>
<area id="manytomany6" coords="7 60"/>
<area id="manytomany7" coords="8 60"/>
</areaspec>
<programlisting><![CDATA[<many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
fetch="select|join"
unique="true|false"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name"
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="manytomany1">
<para>
<literal>column</literal> (opcional): El nombre de la columna de clave
for&#x00e1;nea del elemento.
</para>
</callout>
<callout arearefs="manytomany2">
<para>
<literal>formula</literal> (opcional): Una f&#x00f3;rmula SQL opcional usada
para evaluar el valor de clave for&#x00e1;nea del elemento.
</para>
</callout>
<callout arearefs="manytomany3">
<para>
<literal>class</literal> (requerido): El nombre de la clase asociada.
</para>
</callout>
<callout arearefs="manytomany4">
<para>
<literal>fetch</literal> (opcional - por defecto a <literal>join</literal>):
habilita la recuperaci&#x00f3;n por uni&#x00f3;n externa o selecci&#x00f3;n secuencial para esta
asociaci&#x00f3;n. Este es un caso especial; para una recuperaci&#x00f3;n completamente
temprana (en un solo <literal>SELECT</literal>) de una entidad y sus relaciones
muchos-a-muchos a otras entidades, deber&#x00ed;as habilitar la recuperaci&#x00f3;n
<literal>join</literal> no s&#x00f3;lo de la colecci&#x00f3;n misma, sino tambi&#x00e9;n con este
atributo en el elemento anidado <literal>&lt;many-to-many&gt;</literal>.
</para>
</callout>
<callout arearefs="manytomany5">
<para>
<literal>unique</literal> (opcional): Habilita la generaci&#x00f3;n DDL de una
restricci&#x00f3;n de unicidad para la columna clave for&#x00e1;nea. Esto hace la
multiplicidad de la asociaci&#x00f3;n efectivamente uno a muchos.
</para>
</callout>
<callout arearefs="manytomany6">
<para>
<literal>not-found</literal> (opcional - por defecto a <literal>exception</literal>):
Especifica c&#x00f3;mo ser&#x00e1;n manejadas las claves for&#x00e1;neas que referencian
filas perdidas: <literal>ignore</literal> tratar&#x00e1; una fila perdida como
una asociaci&#x00f3;n nula.
</para>
</callout>
<callout arearefs="manytomany7">
<para>
<literal>entity-name</literal> (opcional): El nombre de entidad de la clase
asociada, como una alternativa a <literal>class</literal>.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Algunos ejemplos, primero, un conjunto de cadenas:
</para>
<programlisting><![CDATA[<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set>]]></programlisting>
<para>
Un bag conteniendo enteros (con un orden de iteraci&#x00f3;n determinado por el
atributo <literal>order-by</literal>):
</para>
<programlisting><![CDATA[<bag name="sizes"
table="item_sizes"
order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag>]]></programlisting>
<para>
Un array de entidades - en este caso, una asociaci&#x00f3;n muchos a muchos:
</para>
<programlisting><![CDATA[<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array>]]></programlisting>
<para>
Un mapa de &#x00ed;ndices de cadenas a fechas:
</para>
<programlisting><![CDATA[<map name="holidays"
table="holidays"
schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>]]></programlisting>
<para>
Una lista de componentes (discutidos en el pr&#x00f3;ximo cap&#x00ed;tulo):
</para>
<programlisting><![CDATA[<list name="carComponents"
table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list>]]></programlisting>
</sect2>
<sect2 id="collections-onetomany">
<title>Asociaciones uno-a-muchos</title>
<para>
Una <emphasis>asociaci&#x00f3;n uno a muchos</emphasis> enlaza las tablas de dos clases
por medio de una clave for&#x00e1;nea, sin intervenci&#x00f3;n de tabla de colecci&#x00f3;n alguna.
Este mapeo pierde cierta sem&#x00e1;ntica de colecciones Java normales:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
Una instancia de la clase entidad contenida no puede pertenecer
a m&#x00e1;s de una instancia de la colecci&#x00f3;n.
</para>
</listitem>
<listitem>
<para>
Una instancia de la clase entidad contenida no puede aparecer en m&#x00e1;s
de un valor del &#x00ed;ndice de colecci&#x00f3;n.
</para>
</listitem>
</itemizedlist>
<para>
Una asociaci&#x00f3;n de <literal>Product</literal> a <literal>Part</literal> requiere la
existencia de una columna clave for&#x00e1;nea y posiblemente una columna &#x00ed;ndice a la tabla
<literal>Part</literal>. Una etiqueta <literal>&lt;one-to-many&gt;</literal> indica
que &#x00e9;sta es una asociaci&#x00f3;n uno a muchos.
</para>
<programlistingco>
<areaspec>
<area id="onetomany1" coords="2 60"/>
<area id="onetomany2" coords="3 60"/>
<area id="onetomany3" coords="4 60"/>
</areaspec>
<programlisting><![CDATA[<one-to-many
class="ClassName"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name"
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="onetomany1">
<para>
<literal>class</literal> (requerido): El nombre de la clase asociada.
</para>
</callout>
<callout arearefs="onetomany2">
<para>
<literal>not-found</literal> (opcional - por defecto a <literal>exception</literal>):
Especifica c&#x00f3;mo ser&#x00e1;n manejados los identificadores en cach&#x00e9; que referencien
filas perdidas: <literal>ignore</literal> tratar&#x00e1; una fila perdida como una
asociaci&#x00f3;n nula.
</para>
</callout>
<callout arearefs="onetomany3">
<para>
<literal>entity-name</literal> (opcional): El nombre de entidad de la clase
asociada, como una alternativa a <literal>class</literal>.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Observa que el elemento <literal>&lt;one-to-many&gt;</literal> no necesita
declarar ninguna columna. Ni es necesario especificar el nombre de <literal>table</literal>
en ning&#x00fa;n sitio.
</para>
<para>
<emphasis>Nota muy importante:</emphasis> Si la columna clave for&#x00e1;nea de una asociaci&#x00f3;n
<literal>&lt;one-to-many&gt;</literal> es declarada <literal>NOT NULL</literal>, debes
declarar el mapeo de <literal>&lt;key&gt;</literal> <literal>not-null="true"</literal>
o <emphasis>usar una asociaci&#x00f3;n bidireccional</emphasis> con el mapeo de colecci&#x00f3;n
marcado <literal>inverse="true"</literal>. Ver la discusi&#x00f3;n sobre asociaciones
bidireccionales m&#x00e1;s adelante en este cap&#x00ed;tulo.
</para>
<para>
Este ejemplo muestra un mapa de entidades <literal>Part</literal> por nombre
(donde <literal>partName</literal> es una propiedad persistente de <literal>Part</literal>).
Observa el uso de un &#x00ed;ndice basado en f&#x00f3;rmula.
</para>
<programlisting><![CDATA[<map name="parts"
cascade="all">
<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map>]]></programlisting>
</sect2>
</sect1>
<sect1 id="collections-advancedmappings">
<title>Mapeos de colecci&#x00f3;n avanzados</title>
<sect2 id="collections-sorted" revision="2">
<title>Colecciones ordenadas</title>
<para>
Hibernate soporta colecciones implementando <literal>java.util.SortedMap</literal> y
<literal>java.util.SortedSet</literal>. Debes especificar un comparador en el fichero de
mapeo:
</para>
<programlisting><![CDATA[<set name="aliases"
table="person_aliases"
sort="natural">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" sort="my.custom.HolidayComparator">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>]]></programlisting>
<para>
Los valores permitidos del atributo <literal>sort</literal> son <literal>unsorted</literal>,
<literal>natural</literal> y el nombre de una clase que implemente
<literal>java.util.Comparator</literal>.
</para>
<para>
Las colecciones ordenadas realmente se comportan como <literal>java.util.TreeSet</literal> o
<literal>java.util.TreeMap</literal>.
</para>
<para>
Si quieres que la misma base de datos ordene los elementos de colecci&#x00f3;n usa el
atributo <literal>order-by</literal> de los mapeos <literal>set</literal>,
<literal>bag</literal> o <literal>map</literal>. Esta soluci&#x00f3;n est&#x00e1; disponible s&#x00f3;lo
bajo el JDK 1.4 o superior (est&#x00e1; implementado usando <literal>LinkedHashSet</literal> o
<literal>LinkedHashMap</literal>). Esto realiza la ordenaci&#x00f3;n en la consulta SQL,
no en memoria.
</para>
<programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date type="date"/>
</map>]]></programlisting>
<para>
Observa que el valor del atributo <literal>order-by</literal> es una ordenaci&#x00f3;n SQL, no
una ordenaci&#x00f3;n HQL!
</para>
<para>
Las asociaciones pueden incluso ser ordenadas por alg&#x00fa;n criterio arbitrario en tiempo de
ejecuci&#x00f3;n usando un <literal>filter()</literal> de colecci&#x00f3;n.
</para>
<programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
</sect2>
<sect2 id="collections-bidirectional" revision="1">
<title>Asociaciones bidireccionales</title>
<para>
Una <emphasis>asociaci&#x00f3;n bidireccional</emphasis> permite la nevegaci&#x00f3;n desde
ambos "extremos" de la asociaci&#x00f3;n. Son soportados dos tipos de asociaci&#x00f3;n
bidireccional:
<variablelist>
<varlistentry>
<term>uno-a-muchos</term>
<listitem>
<para>
set o bag valorados en un extremo, monovaluados al otro
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>muchos-a-muchos</term>
<listitem>
<para>
set o bag valorados a ambos extremos
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Puedes especificar una asociaci&#x00f3;n bidireccional muchos-a-muchos simplemente
mapeando dos asociaciones muchos-a-muchos a la misma tabla de base de datos
y declarando un extremo como <emphasis>inverse</emphasis> (cu&#x00e1;l de ellos es tu
elecci&#x00f3;n, pero no puede ser una colecci&#x00f3;n indexada).
</para>
<para>
He aqu&#x00ed; un ejemplo de una asociaci&#x00f3;n bidireccional muchos-a-muchos; cada categor&#x00ed;a
puede tener muchos &#x00ed;tems y cada &#x00ed;tem puede estar en muchas categor&#x00ed;as:
</para>
<programlisting><![CDATA[<class name="Category">
<id name="id" column="CATEGORY_ID"/>
...
<bag name="items" table="CATEGORY_ITEM">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</bag>
</class>
<class name="Item">
<id name="id" column="CATEGORY_ID"/>
...
<!-- inverse end -->
<bag name="categories" table="CATEGORY_ITEM" inverse="true">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</class>]]></programlisting>
<para>
Los cambios hechos s&#x00f3;lo al extremo inverso de la asociaci&#x00f3;n <emphasis>no</emphasis>
son persistidos. Esto significa que Hibernate tiene dos representaciones en memoria
para cada asociaci&#x00f3;n bidireccional, una enlaza de A a B y otra enlaza de B a A.
Esto es m&#x00e1;s f&#x00e1;cil de entender si piensas en el modelo de objetos de Java y c&#x00f3;mo
creamos una relaci&#x00f3;n muchos-a-muchos en Java:
</para>
<programlisting><![CDATA[
category.getItems().add(item); // The category now "knows" about the relationship
item.getCategories().add(category); // The item now "knows" about the relationship
session.persist(item); // The relationship won't be saved!
session.persist(category); // The relationship will be saved]]></programlisting>
<para>
El lado no-inverso se usa para salvar la representaci&#x00f3;n en memoria a la base de datos.
</para>
<para>
Puedes definir una asociaci&#x00f3;n bidireccional uno-a-muchos mapeando una asociaci&#x00f3;n uno-a-muchos
a la misma columna (o columnas) de tabla como una asociaci&#x00f3;n muchos-a-uno y declarando el
extremo multivaluado <literal>inverse="true"</literal>.
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="eg.Child">
<id name="id" column="id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>]]></programlisting>
<para>
Mapear un extremo de una asociaci&#x00f3;n con <literal>inverse="true"</literal> no afecta
la operaci&#x00f3;n de cascadas; &#x00e9;stos son conceptos ortogonales!
</para>
</sect2>
<sect2 id="collections-indexedbidirectional">
<title>Asociaciones bidireccionales con colecciones indexadas</title>
<para>
Requiere especial consideraci&#x00f3;n una asociaci&#x00f3;n bidireccional donde un extremo est&#x00e9; representado
como una <literal>&lt;list&gt;</literal> o <literal>&lt;map&gt;</literal>. Si hay una propiedad
de la clase hija que mapee a la columna &#x00ed;ndice, no hay problema, podemos seguir usando
<literal>inverse="true"</literal> en el mapeo de la colecci&#x00f3;n:
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children" inverse="true">
<key column="parent_id"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<property name="name"
not-null="true"/>
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>]]></programlisting>
<para>
Pero, si no existe tal proiedad en la clase hija, no podemos pensar en la asociaci&#x00f3;n como
verdaderamente bidireccional (hay informaci&#x00f3;n en un extremo de la asociaci&#x00f3;n que no est&#x00e1;
disponible en el otro extremo). En este caso, no podemos mapear la colecci&#x00f3;n con
<literal>inverse="true"</literal>. En cambio, podr&#x00ed;amos usar el siguiente mapeo:
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children">
<key column="parent_id"
not-null="true"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
insert="false"
update="false"
not-null="true"/>
</class>]]></programlisting>
<para>
Nota que, en este mapeo, el extremo de la asociaci&#x00f3;n valuado en colecci&#x00f3;n es responsable de las
actualizaciones a la clave for&#x00e1;nea.
</para>
</sect2>
<sect2 id="collections-ternary">
<title>Asociaciones ternarias</title>
<para>
Hay tres enfoques posibles para mapear una asociaci&#x00f3;n ternaria.
Una es usar un <literal>Map</literal> con una asociaci&#x00f3;n como su &#x00ed;ndice:
</para>
<programlisting><![CDATA[<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map>]]></programlisting>
<programlisting><![CDATA[<map name="connections">
<key column="incoming_node_id"/>
<map-key-many-to-many column="outgoing_node_id" class="Node"/>
<many-to-many column="connection_id" class="Connection"/>
</map>]]></programlisting>
<para>
Un segundo enfoque es simplemente remodelar la asociaci&#x00f3;n como una clase de entidad.
Este es el enfoque que usamos m&#x00e1;s comunmente.
</para>
<para>
Una alternativa final es usar elementos compuestos, que discutiremos m&#x00e1;s adelante.
</para>
</sect2>
<sect2 id="collections-idbag" revision="1">
<title><literal>Usando un &lt;idbag&gt;</literal></title>
<para>
Si has adoptado completamente nuestra visi&#x00f3;n de que las claves compuestas son una
cosa mala y que las entidades deben tener identificadores sit&#x00e9;ticos (claves delegadas),
entonces podr&#x00ed;as encontrar un poco raro que todas las asociaciones muchos a muchos y
las colecciones de valores que hemos mostrado hasta ahora mapeen a tablas con claves
compuestas! Ahora, este punto es discutible; una tabla de pura asociaci&#x00f3;n no parece
beneficiarse demasiado de una clave delegada (aunque s&#x00ed; <emphasis>podr&#x00ed;a</emphasis> una
colecci&#x00f3;n de valores compuestos). Sin embargo, Hibernate provee una funcionalidad que
te permite mapear asociaciones muchos a muchos y colecciones de valores a una tabla con
una clave delegada.
</para>
<para>
El elemento <literal>&lt;idbag&gt;</literal> te permite mapear una <literal>List</literal>
(o <literal>Collection</literal>) con sem&#x00e1;ntica de bag.
</para>
<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
<collection-id column="ID" type="long">
<generator class="sequence"/>
</collection-id>
<key column="PERSON1"/>
<many-to-many column="PERSON2" class="Person" fetch="join"/>
</idbag>]]></programlisting>
<para>
Como puedes ver, un <literal>&lt;idbag&gt;</literal> tiene un generador de id sint&#x00e9;tico,
igual que una clase de entidad! Una clave delegada diferente se asigna a cada fila
de la colecci&#x00f3;n. Hibernate no provee ning&#x00fa;n mecanismo para descubrir el valor de clave
delegada de una fila en particular, sin embargo.
</para>
<para>
Observa que el rendimiento de actualizaci&#x00f3;n de un <literal>&lt;idbag&gt;</literal> es
<emphasis>mucho</emphasis> mejor que el de un <literal>&lt;bag&gt;</literal> regular!
Hibernate puede localizar filas individuales eficientemente y actualizarlas o borrarlas
individualmente, igual que si fuese una lista, mapa o conjunto.
</para>
<para>
En la implementaci&#x00f3;n actual, la estrategia de generaci&#x00f3;n de identificador
<literal>native</literal> no est&#x00e1; soportada para identificadores de colecciones
<literal>&lt;idbag&gt;</literal>.
</para>
</sect2>
</sect1>
<!--undocumenting this stuff -->
<!--sect1 id="collections-heterogeneous">
<title>Heterogeneous Associations</title>
<para>
The <literal>&lt;many-to-any&gt;</literal> and <literal>&lt;index-many-to-any&gt;</literal>
elements provide for true heterogeneous associations. These mapping elements work in the
same way as the <literal>&lt;any&gt;</literal> element - and should also be used
rarely, if ever.
</para>
</sect1-->
<sect1 id="collections-example" revision="1">
<title>Ejemplos de colecci&#x00f3;n</title>
<para>
Las secciones previas son bastantes confusas. As&#x00ed; que miremos un ejemplo.
Esta clase:
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
public class Parent {
private long id;
private Set children;
public long getId() { return id; }
private void setId(long id) { this.id=id; }
private Set getChildren() { return children; }
private void setChildren(Set children) { this.children=children; }
....
....
}]]></programlisting>
<para>
tiene una colecci&#x00f3;n de instancias de <literal>Child</literal>.
Si cada hijo tiene como mucho un padre, el mapeo m&#x00e1;s natural es
una asociaci&#x00f3;n uno-a-muchos:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Esto mapea a las siguientes definiciones de tablas:
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
<para>
Si el padre es <emphasis>requerido</emphasis>, usa una asociaci&#x00f3;n bidireccional
uno-a-muchos:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Observa la restricci&#x00f3;n <literal>NOT NULL</literal>:
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null
primary key,
name varchar(255),
parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
<para>
Alternativamente, si absolutamente insistes que esta asociaci&#x00f3;n debe ser unidireccional,
puedes declarar la restricci&#x00f3;n <literal>NOT NULL</literal> en el mapeo de
<literal>&lt;key&gt;</literal>:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
En la otra mano, si un hijo pudiera tener m&#x00fa;ltiples padres, ser&#x00ed;a apropiada
una asociaci&#x00f3;n muchos-a-muchos:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" table="childset">
<key column="parent_id"/>
<many-to-many class="Child" column="child_id"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
Definiciones de tabla:
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null,
child_id bigint not null,
primary key ( parent_id, child_id ) )
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
<para>
Para m&#x00e1;s ejemplos y un paseo completo a trav&#x00e9;s del mapeo de relaciones padre/hijo,
ver <xref linkend="example-parentchild"/>.
</para>
<para>
Son posibles mapeos de asociaci&#x00f3;n a&#x00fa;n m&#x00e1;s complejos. Catalogaremos todas las posibilidades
en el pr&#x00f3;ximo cap&#x00ed;tulo.
</para>
</sect1>
</chapter>