Hibernate.orgCommunity Documentation

Capítulo 8. Mapeo de componentes

8.1. Objetos dependientes
8.2. Colecciones de objetos dependientes
8.3. Componentes como índices de Mapeo
8.4. Componentes como identificadores compuestos
8.5. Componentes dinámicos

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

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
>

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
>

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.

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.

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

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
>

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

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.