Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 9. Mapeamento de Componentes

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

## 9.1. Objetos dependentes

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

    >

## 9.2. Coleções de objetos dependentes

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

    >

## Importante

Se você definir um `Set` de elementos compostos, é muito importante implementar `equals()` e `hashCode()` corretamente.

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.

## 9.3. Componentes como índices de Map

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.

## 9.4. Componentes como identificadores compostos

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

- Ele deve implementar `java.io.Serializable`.

- Ele deve re-implementar `equals()` e `hashCode()`, consistentemente com a noção de igualdade de chave composta do banco de dados.

## Nota

No Hibernate 3, o segundo requisito não é um requisito absolutamente necessário. Mas atenda ele de qualquer forma.

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

    >

## Dica

O elemento `column` é uma alternativa para a função `column` em todos os lugares. O uso do elemento `column` apenas fornece mais opçöes de declaração, das quais são úteis quando utilizando `hbm2ddl`.

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 `OrderLine`s 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

    >

## 9.5. Componentes Dinâmicos

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

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 8. Mapeamento de associações
- Acima
- Principal
- PróximaCapítulo 10. Mapeamento de Herança