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