Classe DAO Genérica

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 compreensão do projeto e o desempenho do banco 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 intensão deste post é comentar um pouco sobre a construção de uma classe DAO genérica .Vamos descrever as algumas das principais 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 e genérica. Ao longo dos anos adquirimos alguma experiência nessa área. Longe de mim esgotar o assunto. Mas este pode ser um bom ponto de partida pra quem está iniciando. Fiquem a vontade pra comentar e sugerir alterações nestes conceitos. Iremos utilizar o C#.NET por entender que facilitará bastante a compreensão e, claro, por ser nossa preferência em linguagem de desenvolvimento. 😉 Vejamos, então, 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 utilizar 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
{
T Alterar(T obj);
void Alterar(IList<T> lst);
void BeginTransaction(); //Inicia transação
void CommitTransaction(); //Grava transação
bool EmUso<U>(string propertyName, ID objectValue);
void Excluir(T obj);
void Excluir(IList<T> lst);
T Inserir(T obj);
void Incluir(IList lst);
IList Listar(String ordem); //listar ordenado por campo
void RollbackTransaction(); //Desfaz transação
T SelecionarPor(String propertyName, Object value);
T SelecionarPorId(ID id); // Seleciona um registro pelo ID
ValidaNotNull(T obj);
}


Alguns comentários sobre os métodos:

  • 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.
  • Operações básicas: incluir, alterar e excluir objetos. 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.
  • 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.
  • Listar(string ordem) – Listar linhas da tabela ordenadas por um campo. O nome do campo é passado ao método no parâmetro ‘ordem’.
  • 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.
  • 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.
  • ValidaNotNull – Este método automatiza o processo de validação dos campos que não podem ser nultos. O método verifica se os campos que não podem ser nulos no objetos estão realmente preenchidos.

Nao existe uma lista padrão de métodos a serem adicionados numa classe DAO genérica. Cada equipe de desenvolvedores deve definir quais métodos são relavantes para essa classe. Pra quem deseja começar a construir um DAO genérico 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.

Novos métodos podem ser criados nas classes-filhas de acordo com as novas necessidade. O mais importante é que a classe DAO genérica, 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 juntamente com os novos métodos criado como no exemplo acima. Ter uma DAO genérica 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 classe DAO genérica. 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.