DAO (Objeto de Acesso a Dados) é um padrão de projeto muito famoso. O objetivo é separar das regras da aplicação a tecnologia de persistência de dados. O padrão DAO interliga a aplicação ao SGBD (Sistema Gerenciador de Banco de Dados). O objetivo maior ao usar este padrão é promover isolamento e flexibilidade, principalmente, quando se deseja, por exemplo, mudar o SGBD. Neste tipo de aplicação nenhuma regra deve estar no SGDB. Ele passa a ser apenas um repositório de dados, ou seja, um local onde os dados são armazenados e consultados. Triggers, procedures, functions, roles e views são dispensáveis neste contexto. Claro que não podemos dispensar as chaves primárias, índices, integridade referencial e tudo mais que define as entidades do banco e ajudam a melhorar o desempenho durante seu uso.
Em posts anteriores falamos sobre quão desgastante pode ser a construção de um DAO genérico e com possibilidades de uso em qualquer banco de dados. Mostramos que, mesmo utilizando o SQL ANSI na construção do DAO, poderemos não ter muito sucesso. Sugerimos, então, a utilização de um framework de persistência nessa construção: o Hibernate. Ele é gratuito, código aberto, bem documentado e possui uma legião de adeptos que podem ajudar os iniciantes nos fóruns e blogs espalhados pelo mundo.
A intenção deste post é comentar um pouco sobre a construção de uma Classe DAO genérica utilizando o Hibernate. Vamos descrever as características que esta classe deve ter para dar maior flexibilidade ao desenvolvedor durante a construção das regras na camada de negócio. Em outras palavras, que métodos e atributos essa classe deve conter para ser realmente útil. Ao longo dos anos adquirimos alguma experiência nessa área. Longe de mim esgotar o assunto. Mas este pode ser um bom roteiro pra quem está iniciando. Fiquem a vontade pra comentar e sugerir alterações nestes conceitos. Iremos utilizar o C#.NET com NHibernate (a versão .NET do Hibernate do Java) por entender que facilitará bastante a compreensão e, claro, por ser nossa preferência em linguagem de desenvolvimento.
É importante que o leitor já tenha alguma experiência com o Hibernate e C#.NET. Este post tratará apenas de conceitos envolvendo a sua utilização.
Vejamos a inteface da nossa classe DAO genérica:
/* T é o tipo da classe que a DAO irá gerenciar e ID é o tipo do campo chave primária. Não utilize chave composta nem chave com dados naturais, ou seja, dados com significado, por exemplo, RG, CPF, etc. O ideal é utilizar um campo numérico auto-incrementável na chave primária.*/
public interface IBaseDAO<T, ID> : IDisposable
{
void BeginTransaction(); //Inicia transação
void CommitTransaction(); //Grava transação
void RollbackTransaction(); //Desfaz transação
T SelecionarPorId(ID id); // Seleciona um registro pelo ID
T Inserir(T obj);
T Alterar(T obj);
void Excluir(T obj);
IList<T> Listar(string ordem); //listar ordenado por campo
ICriteria getCriteria();
ISession getSession();
T Atualiza(T obj);
void Excluir(IList<T> lst);
void Incluir(IList<T> lst);
void Alterar(IList<T> lst);
bool EmUso<U>(string join, string propertyName, ID objectValue);
IList CreateQuery(string hql);
IList<U> CreateQuery<U>(string hql);
T UniqueResult(string hql);
U UniqueResult<U>(string hql);
T SelecionarPor(string propertyName, object value);
}
- Iniciamos com o controle de transação com o banco de dados: BeginTransaction(), CommitTransaction() e RoolbackTransaction().
- SelecionarPorId(ID id): Como toda tabela tem chave primária podemos selecionar uma linha da tabela pelo seu ID, ou seja, o identificador único do registro.
- Temos, após, as três operações básicas de persistência: incluir, alterar e excluir. Note que na inclusão e alteração, há um retorno que é exatamente a linha na qual sofreu persistência. O parâmetro de entrada é a linha a ser persistida e o retorno é a linha após a persistência.
- Listar(string ordem) – Listar linhas da tabela ordenadas por um campo. O nome do campo é passado ao método no parâmetro ‘ordem’.
- getCriteria() – Este método é importante pra quem deseja utilizar ICriteria na montagem dos comandos SQL. Essa forma é muito interessante. Utiliza-se métodos de classes para construir comandos SQL. Ganha-se na independencia de banco de dados. Este método deve ser protected.
- getSession() – Este método nos dá acesso a sessão hibernate que estamos usando no momento. A sessão ou ISession é a conexão estabelecida com o banco de dados. Todos os comandos ao banco de dados são feitos atravéz da sessão, ou seja, por essa conexão (inserir, alterar, listar, transações, excluir, etc.). Poderiamos simplesmente explicitar um método com esta sessão e permitir a manipulação da sessão na camada de negócio. Não é uma boa prática. A idéia é ter uma classe DAO com métodos para manipulação de dados que já utilizam a ISession. Não devemos acessar o ISession diretamente na camada de negócio. Os objetos de acesso a dados devem conter métodos que possam ser utilizados na camada de negócio e esta camada não precisa conhecer ou acessar detalhes dessa implementação. Este método deve ser protected.
- Atualiza() – Este método funciona como um refresh. Todas as alterações feitas no objeto persistente (objeto criado pelo hibernate após leitura da linha do banco de dados) pode ser desfeita executando este método. O retorno é o objeto persistente original.
- Incluir, Alterar e Excluir listas de objetos – Esses métodos recebem uma lista de objetos e realizam as operações respectivas em todos os objetos da lista. Um exemplo da utilização é exclusão de email. Marcamos os que queremos excluir e ao confirmar, a lista é excluída de uma vez só e não um a um.
- EmUso – Este método é útil para verificar se um objeto está na composição de outro. Por exemplo, podemos utilizar este método pra saber se a exclusão de um objeto que é uma linha no banco fere a integridade referencial: verificar se um objeto está em uso em outro. Isso é feito através do inner join e checando se um select lista algum registro no banco. Se listar, o registro não poderá ser excluído.
- CreateQuery – Ambos recebem como parâmetro uma instrução em HQL (Hibernate Query Language). A diferença está no retorno. Claro que a instrução HQL de entrada tem que ser compatível com o retorno. O primeiro recebe uma instrução HQL como parâmetro e retornar uma lista de objetos na forma de System.Array e nessa array, colunas podem ser de qualquer tipo de dados. Quem determina essa saida são os campos enumerados no select do HQL de entrada. Já o segundo CreateQuery recebe um HQL de entrada mas tem quer ter uma saída do tipo <U> definido na assinatura do método.
- UniqueResult – Ambos recebem como parâmetro uma instrução HQL. O retorno não é uma lista e sim um resultado em uma única linha ou dado, podendo ser classe ou tipo primitivo. A instrução HQL deve ser construída de acordo com este retorno. O primeiro método retorna um objeto igual ao tipo de dados <T> definido na instância da classe. O segundo permite uma adaptação, mas deve retornar um dado de acordo com o tipo <U> defindo na assinatura do método.
- SelecionarPor() – Este método é bem simples. Recebe como parâmetros o nome de uma propriedade e o valor e retorna o objeto correspondente. Muito útil em relacionamentos um-para-um. Pode-se selecionar um objeto por outra propriedade que não o ID.
Pra quem deseja começar a construir uma classe DAO genérica deve, pelo menos, implementar esses métodos. Deixamos a cargo do leitor criar uma classe que implemente os métodos da interface que comentamos. Na interface que utilizamos em nossos trabalhos existem outros métodos de listagens e manipulação de objetos persistentes (citado acima) e transientes (instâncias criadas pelo programador e que não foram persistidas no banco; muito usadas na transferência de dados entre camadas – DTO). Esses métodos foram construídos após notar que certas operações são recorrentes. Se alguém quiser ver as minhas interfaces e implementações, pode pedir que eu mando.
NA PRÁTICA
Todas as tabelas do banco deverão ter uma classe DAO definida como na classe CidadeDAO abaixo. Note que a nova classe herda as características na nossa BaseDAO<T,ID>. Além da herança, existe uma especialização nessa nova classe. O método SelecionarPorNome() foi criado utilizando recursos da super-classe: getCriteria().
public class CidadeDAO : BaseDAO<Cidade,long>
{
public Cidade SelecionarPorNome(Uf uf, string nomeCidade)
{
ICriteria crit = this.getCriteria()
.Add(Expression.Eq(“Descricao”,nomeCidade))
.Add(Expression.Eq(“IdUf”,uf));
return crit.UniqueResult();
}
}
Novos métodos podem ser criados nas classes-filhas de acordo com as novas necessidade. O mais importante é que a classe BaseDAO, que deve ser uma classe abstrata, possui muitos métodos já definidos que podemos usar livremente, reaproveitando código. Na camada de negócio poderemos instanciar esses DAOs, onde métodos da super-classe estão disponíveis jutamente com os novos métodos criado como no exemplo acima.Ter uma BaseDAO bem definida pode ser a diferença entre uma aplicação bem ou mal projetada. Afinal a comunicação com o banco é feita por esta classe já que todas as outras estarão herdando suas características.
Esperamos ter ajudado na construção da sua BaseDAO. Estamos aberto a dúvidas e comentários. Convidamos, também, os interessados a adicionar implementações para os métodos acima e sugerir métodos interessantes e úteis para enriquecer nosso exemplo de DAO genérico.
Clique aqui e veja continuação deste post.
Oi!
Estou iniciando em programação JAVA, em resumo, o que de fato pode ou deve conter a DAO.
Tenho que sair de uma estrutura não em camadas para o modelo MVC e tenho dúvida de como fazer as divisãoes das classes, conexões entre pacotes etc.
Jã pesquisei mas não consigo encontrar nada de forma clara para trabalhar com MVC.
Poderia me ajudar?
Obrigada.