Hibernate.orgCommunity Documentation

Capítulo 8. Mapeamento de associações

8.1. Introdução
8.2. Associações Unidirecionais
8.2.1. Muitos-para-um
8.2.2. Um-para-um
8.2.3. Um-para-muitos
8.3. Associações Unidirecionais com tabelas associativas
8.3.1. Um-para-muitos
8.3.2. Muitos-para-um
8.3.3. Um-para-um
8.3.4. Muitos-para-muitos
8.4. Associações Bidirecionais
8.4.1. Um-para-muitos/muitos-para-um
8.4.2. Um-para-um
8.5. Associações Bidirecionais com tabelas associativas
8.5.1. Um-para-muitos/muitos-para-um
8.5.2. Um para um
8.5.3. Muitos-para-muitos
8.6. Mapeamento de associações mais complexas

Os mapeamentos de associações são, geralmente, os mais difíceis de se acertar. Nesta seção nós examinaremos pelos casos canônicos um por um, começando com mapeamentos unidirecionais e considerando os casos bidirecionais. Usaremos Person e Address em todos os exemplos.

Classificaremos as associações pela sua multiplicidade e se elas mapeiam ou não uma intervenção na tabela associativa.

O uso de chaves externas anuláveis não é considerado uma boa prática na modelagem de dados tradicional, assim todos os nossos exemplos usam chaves externas anuláveis. Esta não é uma exigência do Hibernate, e todos os mapeamentos funcionarão se você remover as restrições de anulabilidade.

Uma associação bidirecional muitos-para-um é o tipo mais comum de associação. A seguinte amostra ilustra o relacionamento padrão pai/filho. )


<class name="Person">
    <id name="id" column="personId">
        <generator class="native"/>
    </id>
    <many-to-one name="address" 
        column="addressId"
        not-null="true"/>
</class>

<class name="Address">
    <id name="id" column="addressId">
        <generator class="native"/>
    </id>
    <set name="people" inverse="true">
        <key column="addressId"/>
        <one-to-many class="Person"/>
    </set>
</class
>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
        

Se você usar uma List ou outra coleção indexada, você precisará especificar a coluna key da chave externa como not null. O Hibernate administrará a associação do lado da coleção para que seja mantido o índice de cada elemento da coleção (fazendo com que o outro lado seja virtualmente inverso ajustando update="false" e insert="false"):


<class name="Person">
   <id name="id"/>
   ...
   <many-to-one name="address"
      column="addressId"
      not-null="true"
      insert="false"
      update="false"/>
</class>

<class name="Address">
   <id name="id"/>
   ...
   <list name="people">
      <key column="addressId" not-null="true"/>
      <list-index column="peopleIdx"/>
      <one-to-many class="Person"/>
   </list>
</class
>

Caso uma coluna chave externa adjacente for NOT NULL, é importante que você defina not-null="true" no elemento <key> no mapeamento na coleção se a coluna de chave externa para NOT NULL. Não declare como not-null="true" apenas um elemento aninhado <column>, mas sim o elemento <key>.

Uniões de associações mais complexas são extremamente raras. O Hibernate possibilita o tratamento de mapeamentos mais complexos, usando fragmentos de SQL embutidos no documento de mapeamento. Por exemplo, se uma tabela com informações de dados históricos de uma conta define as colunas accountNumber, effectiveEndDate e effectiveStartDate, mapeadas assim como segue:


<properties name="currentAccountKey">
    <property name="accountNumber" type="string" not-null="true"/>
    <property name="currentAccount" type="boolean">
        <formula
>case when effectiveEndDate is null then 1 else 0 end</formula>
    </property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>

Então nós podemos mapear uma associação para a instância atual, aquela com effectiveEndDate nulo, usando:


<many-to-one name="currentAccountInfo"
        property-ref="currentAccountKey"
        class="AccountInfo">
    <column name="accountNumber"/>
    <formula
>'1'</formula>
</many-to-one
>

Em um exemplo mais complexo, imagine que a associação entre Employee e Organization é mantida em uma tabela Employment cheia de dados históricos do trabalho. Então a associação do funcionário mais recentemente e empregado, aquele com a mais recente startDate, deve ser mapeado desta maneira:


<join>
    <key column="employeeId"/>
    <subselect>
        select employeeId, orgId 
        from Employments 
        group by orgId 
        having startDate = max(startDate)
    </subselect>
    <many-to-one name="mostRecentEmployer" 
            class="Organization" 
            column="orgId"/>
</join
>

Esta funcionalidade permite um grau de criatividade e flexibilidade, mas geralmente é mais prático tratar estes tipos de casos, usando uma pesquisa HQL ou uma pesquisa por critério.