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 |
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:
http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/pdf/nhibernate_reference.pdf
José Carlos Macoratti