Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 9. Mapeo de componentes

La noción de un componente se reutiliza en muchos contextos diferentes, para propósitos diferentes a través de Hibernate.

## 9.1. 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 y no a componentes a nivel de arquitectura. Por ejemplo, puede modelar una persona así:

    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;

        }

        ......

        ......

    }

    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;

        }

    }

Ahora `Name` puede ser persistido como un componente de `Person`. `Name` define métodos getter y setter para sus propiedades persistentes, pero no necesita declarar ninguna interfaz ni propiedades identificadoras.

Nuestro mapeo de Hibernate se vería así:

    <class name="eg.Person" table="person">

        <id name="Key" column="pid" type="string">

            <generator class="uuid"/>

        </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

    >

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ólamente "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 es apropiado para la mayoría de propósitos.

Las propiedades de un componente pueden ser de cualquier tipo de Hibernate (colecciones, asociaciones muchos-a-uno, otros componentes, etc). Los componentes anidados no deben ser considerados como un uso exótico. Hibernate está concebido para soportar un modelo de objetos muy detallado.

El elemento `<component>` permite un subelemento `<parent>` que mapea una propiedad de la clase del componente como una referencia a la entidad contenedora.

    <class name="eg.Person" table="person">

        <id name="Key" column="pid" type="string">

            <generator class="uuid"/>

        </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

    >

## 9.2. Colecciones de objetos dependientes

Las colecciones de componentes se encuentran soportadas (por ejemplo, un array de tipo `Name`). Declare su colección de componentes remplazando la etiqueta `<element>` por una etiqueta `<composite-element>`:

    <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

    >

## Importante

Si define un `Set` de elementos compuestos, es muy importante implementar `equals()` y `hashCode()` de manera correcta.

Los elementos compuestos pueden contener componentes pero no colecciones. Si su elemento compuesto contiene a su vez componentes, use la etiqueta `<nested-composite-element>`. Este es un caso de una colección de componentes que a su vez tienen componentes. Se debe estar preguntando si una asociación uno-a-muchos es más apropiada. Remodele el elemento compuesto como una entidad - pero observe que aunque el modelo Java es el mismo, el modelo relacional y la semántica de persistencia siguen siendo ligeramente diferentes.

Un mapeo de elemento compuesto no soporta propiedades nulables si está utilizando un `<set>`. No hay una columna clave principal separada en la tabla del elemento compuesto. Hibernate utiliza el valor de cada columna para identificar un registro al borrar objetos, lo cual es imposible con valores nulos. Tiene que 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>`. Este mapeo le 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:

    <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

    >

No puede haber una referencia a la compra del otro lado para la navegación bidireccional de la asociación. Los componentes son tipos de valor y 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):

    <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

    >

Los elementos compuestos pueden aparecer en consultas usando la misma sintáxis que las asociaciones a otras entidades.

## 9.3. Componentes como índices de Mapeo

El elemento `<composite-map-key>` le permite mapear una clase componente como la clave de un `Map`. Asegúrese de sobrescribir `hashCode()` y `equals()` correctamente en la clase componente.

## 9.4. Componentes como identificadores compuestos

Puede utilizar un componente como un identidicador de una clase entidad. Su clase componente tiene que satisfacer ciertos requerimientos:

- Tiene que implementar `java.io.Serializable`.

- Tiene que re-implementar `equals()` y `hashCode()`, consistentemente con la noción de la base de datos de igualdad de clave compuesta.

## Nota

En Hibernate3, aunque el segundo requerimiento no es un requerimiento absolutamente rígido de Hibernate, en todo caso se recomienda.

No puede utilizar un `IdentifierGenerator` para generar claves compuestas. En cambio, la aplicación debe asignar sus propios identificadores.

Use la etiqueta `<composite-id>`, con elementos anidados `<key-property>`, en lugar de la declaración usual `<id>`. Por ejemplo, la clase `OrderLine` tiene una clave principal que depende de la clave principal (compuesta) de `Order`.

    <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

    >

Cualquier clave foránea que referencie la tabla de `OrderLine` también es compuesta. Declare esto en sus mapeos de otras clases. Una asociación a `OrderLine` se mapea así:

    <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

    >

## Sugerencia

El elemento `column` es una alternativa al atributo `column` en cualquier lugar. El uso del elemento `column` simplemente le da más opciones de declaración, las cuales son útiles al utilizar `hbm2ddl`.

Una asociación `muchos-a-muchos` a `OrderLine` también usa la clave foránea compuesta:

    <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

    >

La colección de `OrderLine`s en `Order` utilizaría:

    <set name="orderLines" inverse="true">

        <key>

            <column name="orderId"/>

            <column name="customerId"/>

        </key>

        <one-to-many class="OrderLine"/>

    </set

    >

El elemento `<one-to-many>` declara ninguna columna.

Si `OrderLine` posee una colección por sí misma, tiene también una clave foránea compuesta.

    <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

    >

## 9.5. Componentes dinámicos

También puede mapear una propiedad del tipo `Map`:

    <dynamic-component name="userAttributes">

        <property name="foo" column="FOO" type="string"/>

        <property name="bar" column="BAR" type="integer"/>

        <many-to-one name="baz" class="Baz" column="BAZ_ID"/>

    </dynamic-component

    >

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 también es posible, usando un analizador DOM. También puede acceder y cambiar el metamodelo de tiempo de configuración de Hibernate por medio del objeto `Configuration`.

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 8. Mapeos de asociación
- Subir
- Inicio
- SiguienteCapítulo 10. Mapeo de herencias