.NET - NHibernate a revanche (Gerando os arquivos de mapeamento)


Um dos sabores mais usados do LINQ , pelo menos para o acesso a dados no SQL Server, é (ou foi) o LINQ to SQL; logo em seguida veio o lançamento do Entity Framework e parece que as coisas andam meio confusas com relação ao futuro do LINQ to SQL.

Tudo começou com uma notícia veiculada no blog da equipe da Microsoft :

que gerou muito comentário:

Assim, se tudo se confirmar, o LINQ to SQL esta com os dias contados. (A Microsoft ainda vai dar suporte, bla,bla,bla...)

O que eu acho engraçado é que quem optou em usar o LINQ to SQL em seus projetos não tem outra opção confiável no momento. (Eu particularmente não usaria o instável e pouco confiável Entity Framework para aplicações em produção, pois não tenho vocação para cobaia. E, não estou só nesta questão http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/)

Eu cheguei a ler comparações entre o NHibernate e o Entity Framework nas quais decretava que a ferramenta da Microsoft era superior, isso após a mesma ter acabado de ser lançada. Um absurdo !!!

Nenhum case de sucesso no mercado, nenhuma referência de projeto em produção comercial enquanto que o NHibernate, o porte para Hibernate do Java, embora tenha seus problemas, está há mais tempo no mercado com muita gente usando em produção.

Por falar em NHibernate, ele continua a todo vapor e a comunidade tem agregado ferramentas que tornam o trabalho com a ferramenta mais produtivo e menos tediosa.

Se você conhece o NHibernate já sabe do que estou falando. A ferramenta é muito boa mas o trabalho que dá gerar os arquivos de configuração e mapeamento para grandes projetos não é brincadeira.

Felizmente existem algumas opções de ferramentas que tem o objetivo de efetuar a geração dos arquivos de configuração e mapeamento de forma automática e assim facilitar a utilização do NHibernate. Veja abaixo algumas opções:

Neste artigo eu vou mostrar como usar o MyGeneration para gera os arquivos de mapeamento para o NHibernate tornando assim a sua vida mais fácil e encorajando-o a usar esta poderosa ferramenta que por enquanto é uma opção embassada em projetos de sucesso e que tem uma comunidade ativa.

Gerando os arquivos de mapeamento para o NHibernate

Recursos necessários:

Roteiro de utilização:

Por padrão a instalação do MyGeneration é feita na pasta \Arquivos de Programas\MyGeneration13;(você pode alterar este comportamento)

Existem duas sub-pastas importantes na pasta principal de instalação:

Existem diversos templates para o NHibernate que você pode usar. Eu baixei alguns templates para o NHibernate mas neste artigo eu vou usar o l99_nhibernate por achar que ele é mais completo. (Você pode efetuar testes com os demais templates e usar o que achar mais adequado)

Eu vou criar um banco de dados no SQL Server 2005 Express Edition usando a ferramenta Management Studio Express chamado Macoratti.mdf contendo as seguintes tabelas e relacionamentos: (Não vou dar detalhes de como criar o banco de dados e as tabelas, veja nas referências links de como fazer este serviço)

Observe que cada tabela possui uma chave primária e que temos um modelo de dados simples mas bem normalizado. Isto é muito importante para que o mapeamento seja feito corretamente e funcione na prática. A documentação do NHibernate não recomenda usar chaves compostas em tabelas portanto vamos seguir esse conselho neste nosso primeiro contato com a geração de arquivos de mapeamento.

A seguir temos que efetuar a definição da conexão com o nosso banco de dados, que no meu caso esta no servidor local .\SQLEXPRESS com nome Macoratti.mdf no caso um banco de dados SQL Server , por isso o driver usado foi definido para : Microsoft SQL Server;

Veja a configuração na janela Default Settings aba Connection:

Observe que eu selecionei a linguagem VB .NET que será usada para gerar os arquivos de mapeamento mas poderia ter usado C#.

Após estas configurações clique no botão - Test Connection - e você deverá obter uma mensagem de conexão bem sucedida. Se houver erro verifique a string de conexão e tente novamente.

Vamos agora usar um template adequado para gerar os arquivos de mapeamento. Clique no menu File -> Open e localiza a pasta Templates e no seu interior a sub-pasta NHibernate onde foram copiados os templates para o NHibernate.

A seguir selecione o template  l99_nhibernate conforme abaixo:

Podemos também através do botão Template Browser clicar ícono do globo (On line Template Library) que abrirá uma nova janela;

Navegue até a pasta NHibernate e abra a pasta. A seguir selecione o template com o qual deseja trabalhar. No nosso exemplo : lujan99-1.0.6;

Na próxima tela clique no botão Execute:

Você verá a tela exibindo as entidades do seu banco de dados, no nosso exemplo Macoratti.mdf. Neste momento você pode deve selecionar a entidade que deseja mapear clicando com o botão direito do mouse selecionar a opção Map.

Ao fazer isso serão exibidos as propriedades da entidade mapeada

A seguir podemos visualizar as tabelas e as entidades bem como definir alguns mapeamentos;

Na guia Options podemos alterar/definir o nome do Assembly, do namespace, o prefixo dos membros e definir opções para geração dos arquivos de mapeamento;

Após definirmos para quais entidades desejamos gerar os arquivos de mapeamento estamos prontos para continuar. No nosso exemplo usamos o botão Remap All para mapear todas as entidades;

Ao clicar no botão Generate serão criadas duas pastas na pasta GenerateCode contendo as entidades e os mapeamentos gerados;

Nota: você pode definir outro local para gerar os arquivos de mapeamento.

Nas figuras abaixo vemos os arquivos de mapeamento .hbm e as entidades geradas para o nosso exemplo:

A seguir temos a entidade Clientes gerada pelo MyGeneration;

/*
using MyGeneration/Template/NHibernate (c) by lujan99@usa.net
*/
using System;
using System.Collections;
using System.Collections.Generic;

namespace nh_VB
{
	/// <summary>
	/// Clientes object for NHibernate mapped table 'Clientes'.
	/// </summary>
	[Serializable]
	public class Clientes
	{
		#region Member Variables
		protected Integer _clienteid;
		protected String _nome;
		protected String _endereco;
		protected String _cidade;
		protected String _cep;
		protected String _pais;
		protected String _email;
		protected IList<Pedidos> _pedidos;
		#endregion
		#region Constructors
			
		public Clientes() {}
					
		public Clientes(String nome, String endereco, String cidade, String cep, String pais, String email) 
		{
			this._nome= nome;
			this._endereco= endereco;
			this._cidade= cidade;
			this._cep= cep;
			this._pais= pais;
			this._email= email;
		}

		#endregion
		#region Public Properties
		public  virtual Integer Clienteid
		{
			get { return _clienteid; }
			set {_clienteid= value; }
		}
		public  virtual String Nome
		{
			get { return _nome; }
			set {_nome= value; }
		}
		public  virtual String Endereco
		{
			get { return _endereco; }
			set {_endereco= value; }
		}
		public  virtual String Cidade
		{
			get { return _cidade; }
			set {_cidade= value; }
		}
		public  virtual String Cep
		{
			get { return _cep; }
			set {_cep= value; }
		}
		public  virtual String Pais
		{
			get { return _pais; }
			set {_pais= value; }
		}
		public  virtual String Email
		{
			get { return _email; }
			set {_email= value; }
		}
		public  virtual IList<Pedidos> Pedidos
		{
			get { return _pedidos; }
			set {_pedidos= value; }
		}
		#endregion
		
		#region Equals And HashCode Overrides
		/// <summary>
		/// local implementation of Equals based on unique value members
		/// </summary>
		public override bool Equals( object obj )
		{
			if( this == obj ) return true;
			if( ( obj == null ) || ( obj.GetType() != this.GetType() ) ) return false;
			Clientes castObj = (Clientes)obj;
			return ( castObj != null ) &&
			this._clienteid == castObj.Clienteid;
		}
		/// <summary>
		/// local implementation of GetHashCode based on unique value members
		/// </summary>
		public override int GetHashCode()
		{
			int hash = 57;
			hash = 27 * hash * _clienteid.GetHashCode();
			return hash;
		}
		#endregion
		
	}
}

Abaixo temos o arquivo de mapeamento para a entidade Clientes:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <!--Build: with lujan99@usa.net Nhibernate template-->
  <class name="nh_VB.Clientes,nh_VB" table="Clientes" lazy="true">
    <id name="Clienteid" column="clienteid" type="Integer">
      <generator class="native" />
    </id>
    <property name="Nome" column="nome" type="String" />
    <property name="Endereco" column="endereco" type="String" />
    <property name="Cidade" column="cidade" type="String" />
    <property name="Cep" column="cep" type="String" />
    <property name="Pais" column="pais" type="String" />
    <property name="Email" column="email" type="String" />
    <bag name="Pedidos" inverse="true" lazy="true" cascade="delete">
      <key column="clienteid" />
      <one-to-many class="nh_VB.Pedidos,nh_VB" />
    </bag>
  </class>
</hibernate-mapping>

A seguir temos o arquivo de mapeamento para a entidade Produtos:

Agora  temos o arquivo de mapeamento para a entidade Categorias:

Aqui temos o arquivo de mapeamento para a entidade ItensPedidos. Observe a o relacionamento com as entidades Produtos e Pedidos esta definida em um mapeamento many-to-one:

Finalmente temos o arquivo de mapeamento para a entidade Pedidos onde temos o relacionamento com a entidade Clientes definido no mapeamento many-to-one.

Vejamos a seguir a explicação para alguns dos atributos usados nestes arquivos de mapeamento gerados:

- O atributo name indica o nome da propriedade/campo do objeto;

- O atributo column indica o nome da coluna na base de dados;

- O elemento <generator> indica a forma como o id é gerado. Temos as seguintes possibilidades:.

  1. Increment - Lê o valor máximo da chave primária e incrementa;
  2. Identity - Mapeado para colunas identity no DB2, MySQL, MSSQL, SyBase, Informix;
  3. sequence - Mapeado em sequências no DB2, PostGreSQL, Oracle, SAP DB, Firebird;
  4. hilo - Usa um algoritmo high/low para gerar chaves únicas;
  5. uudi.hex - Usa uma combinação do IP com um timestamp para gerar um identificador único.
  6. guid - Usa o novo system.guid como identificador;
  7. assigned - será gerado pela aplicação;
  8. native - Deixa o banco de dados cuidar da geração;

- O atributo lazy definido como true indica ‘quanto mais tarde melhor’, isto é, a coleção só vai ser preenchida, quando realmente for necessária e quando se acessar a alguma propriedade da coleção que necessite dessa informação. Evitam-se assim acessos desnecessários na base de dados;

- O elemento <key> indica qual é a chave estrangeira. (é o que vai aparecer na cláusula WHERE);

- O elemento <one-to-many> indica a cardinalidade da relação e qual a classe com que esta se relaciona.  

- O atributo cascade indica se as operações devem ser feitas em cascata. Por exemplo, se eu excluir um pedido que tenha itens associados, estes também devem ser excluídos ;   

- O  atributo inverse indica que esta é a direção inversa da relação. Em relações de um-para-muitos ou de muitos-para-um, deve ficar sempre do lado do muitos, em relações  muitos-para-muitos, isto é indiferente. Este atributo serve para que o Nhibernate não tente incluir os dados duas vezes na base de dados, o que geraria uma exceção de violação da chave primária.

Nota: Para termos acesso as coleções , podemos utilizar um dos vários tipos de mapeamento de coleções disponibilizados pelo NHibernate, através dos elementos: <bag>, <set>, <array> e <list>

- O elemento bag representa o iList da classe, é a responsável por cadastrar ou trazer as listas que um determinado objeto tem como propriedade sem a necessidade de mais consultas hql, usando apenas o mapeamento e uma chamada hql. (HQL é uma linguagem que é parecida com o SQL, porém é independente do SGBD ).

Vemos que o trabalho para geração dos arquivos de mapeamento pode ser feito por uma ferramenta como o MyGenaration aumentando assim a produtividade. Existem outras ferramentas como Castle ActiveRecord que é uma implementação do padrão Active Record desenvolvido em .NET em cima do NHibernate que permite a possibilidade de mapeamento de classes via atributos e não dos arquivos xml como também um mecanismo de validação eficiente e flexível.

Aguarde mais artigos sobre o NHibernate em breve...

Eu sei é apenas NHibernate, mas eu gosto...

Referências:


José Carlos Macoratti