NHibernate - Efetuando o mapeamento XML de associações 1:N


No meu artigo  VB .NET - Usando o NHibernate no VB 2008 apresentei o NHibernate suas características e dei um exemplo de utilização no VB 2008 com SQL Server. Foi apenas uma introdução e temos muito o que falar sobre este poderoso framework,e aos poucos irei mostrando os seus recursos.

Neste artigo eu vou mostrar como realizar o mapeamento ORM nos arquivos XML .hbm.xml usados pelo NHibernate para mapear as classes para as tabelas do banco de dados. Neste artigo irei abordar somente o mapeamento um-para-muitos.

Este artigo é baseado na documentação do NHibernate que você pode encontrar em : http://www.hibernate.org/5.html

Você pode consultar também a documentação do Hibernate, o irmão mais velho do NHibernate, visto que os fundamentos são os mesmos.

No meu primeiro artigo eu mostrei o mapeamento mais simples que existe: de uma única tabela com tipos de dados básicos; veja como ficou o arquivo XML:

Obs: Eu estou usando a última versão do NHibernate : NHibernate 2.0.1.GA (para versões anteriores veja como migrar na documentação)

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="usandoNHibernate.funcionario, usandoNHibernate" table="funcionario">
    <id name="Codigo" column="codigo" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="Nome" column="Nome" type="String" length="15" />
    <property name="Salario" column="Salario" type="Int32" length="12" />
  </class>	
</hibernate-mapping>

Neste exemplo gerei o mapeamento da classe funcionario para tabela funcionario onde as propriedades da classe possuíam o mesmo nome dos campos da tabela.

Acontece que na prática geralmente temos mais de uma tabela e as tabelas se relacionam entre si , afinal estamos tratando com banco de dados relacionais. Teremos então que considerar a associação existente entre as entidades e efetuar o mapeamento. Vamos considerar a associação do tipo 1:N.

Efetuando o mapeamento de associações um-para-muitos (1:N)

Vamos começar pelo começo (sic) definindo duas entidades com relacionamento entre si do tipo um-para-muitos e ver como efetuar o mapeamento no NHibernate.

Pensando em termos de tabelas de banco de dados podemos definir o relacionamento um-para-muitos assim:

O relacionamento um-para-muitos é o tipo mais comum de relacionamento. Num relacionamento um-para-muitos, um registro na Tabela A pode ter muitos registros coincidentes na Tabela B, mas um registro na Tabela B tem um só registro coincidente na Tabela A.

Seguindo esta linha de raciocínio vamos definir que um funcionário pode ter muitos dependentes mas que um dependente deve estar associado a somente um funcionário. Desta forma teremos o seguinte: (lembre-se que temos que pensar em termos de objetos)

Considerando o relacionamento entre a entidade Funcionario e Dependente temos que:

- Um funcionário possui um conjunto de n dependentes
- Um dependente esta associado a apenas um funcionário;

 Representando as classes de domínio temos: (para facilitar a leitura não representei os métodos get/set nem os construtores)

using System;
using System.Collections.Generic;
using System.Text;
namespace macoratti
{
    class Funcionario
    {
        private int codigo;
        private String nome;
        private decimal salario;
        private Collection dependentes;
        ........
    }
}
using System;
using System.Collections.Generic;
using System.Text;
namespace macoratti
{
    class Dependente
    {
        private int codigo;
        private String nome;
        private Funcionario funci;
        .......
    }
}
classe Funcionario classe Dependente

Neste exemplo eu vou usar tabelas com nomes de campo diferentes dos definidos nas entidades. Assim teremos uma tabela Funcionario e outra tabela Dependente. Veja abaixo a representação das tabelas:

A seguir temos o arquivo de mapeamento funcionario.hbm.xml :

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="nome_Assembly.Funcionario, nome_Assembly" table="Funcionario">
    <id name="Codigo" column="FUNCI_ID" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="Nome" column="NOME" type="String" length="15" />
    <property name="Salario" column="SALARIO" type="Int32" length="12" />
   <!-- Mapeamento da Coleção de dependentes -->
      <set name="dependentes" inverse="true"  lazy="true" >  
      <key column ="FUNCI_ID" />
      <one-to-may class="nome_Assembly.Dependente, nome_Assembly" /> 
   </class>	
</hibernate-mapping>

Explicando o mapeamento realizado: (nome_Assembly é o nome do seu assembly)

<set name ="dependentes" inverse = "true"  lazy = "true" > 

Para realizar o mapeamento 1-n estamos usando a tag set que representa um coleção de objetos não repetidos que podem ou não estar ordenados;

A seguir o atributo name vai definir a propriedade que esta sendo tratada para realizar o mapeamento 1-n, no caso dependentes;

<key column ="FUNCI_ID" />

No atributo key representamos a coluna da tabela relacionada (no caso a tabela Dependente) que possui a chave estrangeira (FUNCI_ID) para a classe Funcionario; ]

No atributo column informamos o nome da coluna da chave estrangeira (FUNCI_ID) da tabela Dependente.

<one-to-may class="nome_Assembly.Dependente, nome_Assembly" />

A tag <one-to_many> define a classe a qual pertence a coleção de objetos , no nosso exemplo, a classe Dependente.

Observações :

- No elemento <one-to_many> não precisamos declarar o nome nem as colunas da tabela.

- Se o atributo <key> de uma associação <one-to_many> for declarada como NOT NULL o NHibernate pode causar a violação de constraints quando ele criar ou atualizar uma associação. Para evitar este problema você deve usar uma associação bidirecional com o valor do lado many (no set ou bag) definido como inverse="true".

-  O atributo inverse da tag <set> é usado para que o NHibernate saiba como tratar a associação entre duas tabelas; quando inverse for definido como true temos que a ligação do relacionamento entre a associação será de responsabilidade do 'outro lado' da associação. No exemplo acima definimos no mapeamento do conjunto de dependentes de um funcionário o atributo inverse="true" com isso a criação ou atualização do relacionamento entre um dependente e um funcionário será feita durante a persistência ou atualização de um objeto Dependente.

- O atributo lazy foi definido como true isto significa que  só são trazidos os dados que são utilizados, ou seja, são obtidos por demanda;

Por padrão o NHibernate usa o mecanismo lazy loading (busca preguiçosa ou algo parecido) usando um  proxy para associações de um valor.

As coleções podem usar o mecanismo lazy loading significando que elas carregam o seus estados a partir do banco de dados somente quando a aplicação precisa acessá-los. A inicialização ocorre de forma transparente para o usuário.

Assim, na classe Funcionario os dados do atributo dependentes somente seriam retornados com uma chamada a eles.

Obs: existem alguns problemas decorrente desse mecanismo para os quais você deve estar atento. Consulte a documentação para mais detalhes.

Em outro artigo pretendo abordar o mapeamento muitos-para-muitos. Até mais ver...

referências:


José Carlos Macoratti