Hibernate.orgCommunity Documentation

Capítulo 9. Mapeamento de Componentes

9.1. Objetos dependentes
9.2. Coleções de objetos dependentes
9.3. Componentes como índices de Map
9.4. Componentes como identificadores compostos
9.5. Componentes Dinâmicos

A noção de componente é re-utilizada em vários contextos diferentes, para propósitos diferentes, pelo Hibernate.

Um componente é um objeto contido que é persistido como um tipo de valor, não uma referência de entidade. O termo "componente" refere-se à noção de composição da orientação a objetos e não a componentes no nível de arquitetura. Por exemplo, você pode modelar uma pessoa desta maneira:

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;
    }
}

Agora Name pode ser persistido como um componente de Person. Note que Name define métodos getter e setter para suas propriedades persistentes, mas não necessita declarar nenhuma interface ou propriedades identificadoras.

Nosso mapeamento do Hibernate seria semelhante a este:


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

A tabela person teria as seguintes colunas pid, birthday, initial, first and last.

Assim como todos tipos por valor, componentes não suportam referências cruzadas. Em outras palavras, duas persons podem ter o mesmo nome, mas os dois objetos person podem ter dois objetos de nome independentes, apenas "o mesmo" por valor. A semântica dos valores null de um componente são ad hoc. No recarregameno do conteúdo do objeto, o Hibernate entenderá que se todas as colunas do componente são null, então todo o componente é null. Isto seria o certo para a maioria dos propósitos.

As propriedades de um componente podem ser de qualquer tipo do Hibernate(coleções, associações muitos-para-um, outros componentes, etc). Componentes agrupados não devem ser considerados luxo. O Hibernate tem a intenção de suportar um modelo de objetos fine-grained (muito bem granulados).

O elemento <component> permite um sub-elemento <parent> mapeie uma propriedade da classe do componente como uma referencia de volta para a entidade que o contém.


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

Coleções de componentes são suportadas (ex.: uma matriz de tipo Name). Declare a sua coleção de componentes substituindo a tag<element> pela tag <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
>

Elementos compostos podem conter componentes mas não coleções. Se o seu elemento composto tiver componentes , use a tag <nested-composite-element>. Este é um caso bastante exótico – coleções de componentes que por si própria possui componentes. Neste momento você deve estar se perguntando se uma associação de um-para-muitos seria mais apropriada. Tente remodelar o elemento composto como uma entidade – mas note que mesmo pensando que o modelo Java é o mesmo, o modelo relacional e a semântica de persistência ainda são diferentes.

Um mapeamento de elemento composto não suporta propriedades capazes de serem null se você estiver usando um <set>. Não existe coluna chave primária separada na tabela de elemento composto. O Hibernate tem que usar cada valor das colunas para identificar um registro quando estiver deletando objetos, que não é possível com valores null. Você tem que usar um dos dois, ou apenas propriedades não null em um elemento composto ou escolher uma <list>, <map>, <bag> ou <idbag>.

Um caso especial de elemento composto é um elemento composto com um elemento <many-to-one> aninhado. Um mapeamento como este permite que você mapeie colunas extras de uma tabela de associação de muitos-para-muitos para a classe de elemento composto. A seguinte associação de muitos-para-muitos de Order para um Item onde purchaseDate, price e quantity são propriedades da associação:


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

Não pode haver uma referência de compra no outro lado, para a navegação da associação bidirecional. Lembre-se que componentes são tipos por valor e não permitem referências compartilhadas. Uma classe Purchase simples pode estar no conjunto de uma classe Order, mas ela não pode ser referenciada por Item no mesmo momento.

Até mesmo associações ternárias (ou quaternária, etc) são possíveis:


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

Elementos compostos podem aparecer em pesquisas usando a mesma sintaxe assim como associações para outras entidades.

O elemento <composite-map-key> permite você mapear uma classe componente como uma chave de um Map. Tenha certeza que você sobrescreveu hashCode() e equals() corretamente na classe componente.

Você pode usar um componente como um identificador de uma classe entidade. Sua classe componente deve satisfazer certos requisitos:

Você não pode usar um IdentifierGenerator para gerar chaves compostas. Ao invés disso, o aplicativo deve gerenciar seus próprios identificadores.

Use a tag <composite-id>, com elementos <key-property> aninhados, no lugar da declaração <id> de costume. Por exemplo, a classe OrderLine possui uma chave primária que depende da chave primária (composta) 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
>

Agora, qualquer chave exterior referenciando a tabela OrderLine também será composta. Você deve declarar isto em seus mapeamentos para outras classes. Uma associação para OrderLine seria mapeada dessa forma:


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

Uma associação many-to-many para many-to-many também usa a chave estrangeira composta:


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

A coleção de OrderLines em Order usaria:


<set name="orderLines" inverse="true">
    <key>
        <column name="orderId"/>
        <column name="customerId"/>
    </key>
    <one-to-many class="OrderLine"/>
</set
>

O elemento <one-to-many> não declara colunas.

Se OrderLine possui uma coleção, ela também tem uma chave externa composta.


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

Você pode até mesmo mapear uma propriedade do 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
>

A semântica de um mapeamento <dynamic-component> é idêntica à <component>. A vantagem deste tipo de mapeamento é a habilidade de determinar as propriedades atuais do bean no momento da implementação, apenas editando o documento de mapeamento. A Manipulação em tempo de execução do documento de mapeamento também é possível, usando o parser DOM. Até melhor, você pode acessar,e mudar, o tempo de configuração do metamodelo do Hibernate através do objeto Configuration.