Mapeamento Objeto-Relacional com NHibernate
Neste artigo conheceremos um dos principais frameworks de mapeamento objeto-relacional do .NET Framework, o NHibernate, e como realizar o mapeamento fluente utilizando a ferramenta auxiliar Fluent NHibernate.
O Mapeamento Objeto-Relacional é um dos tópicos mais em evidência na Tecnologia da Informação atualmente. Isso ocorre porque as linguagens de programação Orientadas a Objetos são as mais utilizadas, e as aplicações precisam dos Bancos de Dados Relacionais para armazenamento de dados. Essa diferença de paradigma leva a problemas que precisam ser contornados pela aplicação, e uma das formas de evitá-los é o ORM.
O NHibernate, por sua vez, é um dos ORMs mais antigos do mercado de desenvolvimento .NET. Hoje em dia, porém, há outras opções, como o LINQ to SQL e Entity Framework, que fazem essa ponte entre o modelo relacional e o orientado a objetos. Então, porque utilizar o NHibernate? Veremos algumas vantagens da utilização do NHibernate ao longo do artigo, mas na realidade é uma questão de escolha. O NHibernate se destaca devido aos seus muitos recursos, sem contar os projetos paralelos que visam auxiliar o NHibernate principal. Além disso, ele foi concebido de forma a permitir adaptações de acordo com a necessidade do usuário: se não estivermos satisfeitos com o tipo de mapeamento disponível, por exemplo, é possível a criação de outro, entre outras possibilidades.
É nesse contexto que o Fluent NHibernate se encaixa – o NHibernate original não permite o mapeamento fluente, apenas por XML. É por isso que o Fluent é uma ferramenta tão interessante, que está com seu uso em disseminação, atualmente. A ideia é que se abstraia o mapeamento via XML, muito trabalhoso, e isso seja feito via código, com a utilização de métodos. Ao longo desse artigo, vamos trazer em profundidade o funcionamento do Fluent NHibernate, juntamente com as possibilidades que essa ferramenta nos traz.
Mapeamento Objeto-Relacional
Praticamente todas as aplicações criadas e utilizadas atualmente possuem alguma interação com dados, e isso significa, normalmente, bancos de dados relacionais. A grande sacada desse tipo de mapeamento é trazer as tabelas das bases de dados para a realidade do desenvolvimento Orientado a Objetos (OO), transformando em classes do domínio da aplicação. Esse tipo de atitude facilita muito o desenvolvimento quando estamos lidando com os dados.
Mas, afinal, como o Mapeamento Objeto-Relacional funciona? Primeiramente, precisamos entender quais os problemas que os diferentes paradigmas (OO e Relacional) podem nos trazer:
- Granularidade: Nem sempre o modelo de negócios da aplicação será exatamente igual às tabelas da base de dados; normalmente, a aplicação traz mais classes do que há tabelas na base;
- Unicidade: Nos bancos de dados relacionais, a chave primária garante a unicidade dos dados, sendo que não há um equivalente para objetos em OO;
- Herança e Polimorfismo: Um dos principais conceitos de OO, não está presente nos bancos de dados relacionais (embora alguns deles definam sub e supertipos (BOX 1));
- Busca aos dados: A navegação entre os dados dentro da aplicação .NET pode ser comparada a uma rede de computadores: um objeto leva a outro. Nos bancos de dados relacionais, esse tipo de funcionamento não ocorre, preferindo trazer todos os dados de uma vez através de cláusulas JOIN ou similares, minimizando o número de acessos à base;
- Associações: As associações entre classes em OO são referências unidirecionais, enquanto nos bancos de dados relacionais temos a noção de chave estrangeira;
Alguns bancos de dados trazem o conceito de subtipos e supertipos, que formam uma espécie de herança dentro dos bancos de dados relacionais. A ideia é que duas entidades tenham uma certa hierarquia, e esse conceito permite o compartilhamento de campos entre duas ou mais tabelas. Em outras palavras, subtipos e supertipos são simplesmente sobre generalização e especialização das tabelas do banco.
A partir desses conceitos, é possível entendermos como o Mapeamento Objeto-Relacional de forma mais clara. Sem esse mapeamento, como mostra a Listagem 1, temos a execução de um comando SQL que nos retorna um resultado na forma de um array. Entretanto, não temos nenhum tipo de conhecimento sobre os dados que vieram, se estão corretos, se são exatamente o que esperamos etc., o que pode levar a uma série de problemas de execução na aplicação, especialmente se temos um servidor específico de banco de dados.
String comandoStr = “Select * from Pessoa”;
DbCommand comando = new DbCommand (conexao, comandoStr);
Result res = comando.Execute();
String nome = res[0][“Nome”];
No caso dessa listagem poderíamos ter problemas de tipos, por exemplo, caso o campo “Nome” não seja uma string, ou algum erro, caso o campo não exista ou o comando não execute. Normalmente, esse tipo de problema é contornado com a utilização de blocos try/catch, mas eles não resolvem o problema da execução correta da aplicação – apenas evitam que ela falhe. Já no código a seguir, temos o conceito de repositório de dados: a classe Pessoa está representando os dados da base de dados. Note que o acesso é muito mais claro, e que essa abordagem não é tão susceptível a erros como a anterior. Além disso, fica muito mais claro para o desenvolvedor entender com o que ele está lidando. É claro que os erros de acesso à base ainda podem ocorrer, mas essa abordagem elimina os erros e tipos de dados e outros erros que possam acontecer no domínio da aplicação.
Pessoa p = repositorio.GetPessoa(id);
String nome = p.Nome;
Os Frameworks de ORM são ferramentas muito úteis, mas é preciso cuidado. Algumas das bases de dados piores são aquelas que confiam muito nas capacidades da ORM, sem que haja um design adequado dos elementos. Isso gera principalmente problemas de desempenho, o que pode prejudicar a aplicação no mercado."
[...] continue lendo...Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo