Design Pattern: criando uma classe DAO genérica

AGRADEÇA AO AUTOR COMPARTILHE!

Há algum tempo atrás era muito comum encontrar a lógica de persistência de dados junto das regras de negócio. Isto aumentava consideravelmente a complexidade de entendimento e manutenção do projeto. Com o passar do tempo as técnicas de desenvolvimento amadureceram e deram origem a alguns padrões de projeto fortemente utilizados nos dias de hoje, como por exemplo o padrão de projeto MVC (Model – View – Controller). Este design pattern divide a aplicação em camadas com responsabilidades específicas, sendo:

  • Model: responsável por abrigar as lógicas de persistência e conversão de dados do SGDB em objetos da aplicação, de forma genérica ou não.
  • View: objetivo de abrigar todas as informações visuais contendo entradas de dados, regras de visualização, entre outros.
  • Controller: responsável por aplicar as regras de negócio da aplicação.

mvc_crudb11

No contexto acima, é possível comparar o design pattern DAO (Data Access Object) com a camada Model do padrão MVC, uma vez que o mesmo surgiu da necessidade de separar as lógicas de persistência das lógicas de negócio da aplicação. Este padrão tem como objetivo trocar informações de forma independente com o SGBD e fornecer os conceitos básicos para a realização de CRUDs ou recursos mais complexas. Quando aplicado de forma correta, toda a necessidade de busca e cadastro de informações é abstraída, tornando o processo de manipulação de dados das entidades transparente às demais camadas do sistema, ou seja, uma classe Pessoa pode ter um DAO dedicado a realizar operações específicas no SGBD, como por exemplo, consultar pessoas por CPF, idade, etc.

O problema

Levando em consideração que uma classe X pode ter uma representação XDAO responsável por executar as suas lógicas de persistência, imagine se houver 100, 150, 1000 entidades que necessitam de integração com o banco de dados. Seria necessário criar a mesma quantidade de classes DAO para executar as regras de persistência destas entidades?

A solução

Graças ao conceito de Generics presente no Java, é possível criar uma classe DAO que será capaz de abstrair um tipo qualquer de entidade da aplicação e executar comandos específicos, eliminando assim os chamados boilerplates (código clichê). Desta forma, se um número N de entidades do sistema tem características semelhantes, ao invés de se criar N classes DAO, cada uma representando um modelo, a aplicação passaria a ter apenas uma classe genérica abstraindo as funcionalidades em comum entre um grupo específico de objetos que necessitam de comunicação com o banco de dados.

Obs: Para minimizar o esforço de criação das queries de banco de dados e elevar a produtividade deste artigo, será utilizada a biblioteca JPA (Java Persistence API) a qual abstrai os métodos de CRUD de forma simples e legível.

Recursos necessários

  1. IDE de sua preferência
  2. JARs da biblioteca JPA, implementação Hibernate
  3. Servidor MYSQL 5
  4. JAR do conector MYSQL

Estrutura do artigo

  1. Criação do projeto de exemplo e configuração da biblioteca JPA
  2. Criando a classe de conexão com o banco de dados
  3. Criação das classes de modelo
  4. Criação da classe DAO genérica
  5. Testando a classe DAO genérica

1. Criação do projeto de exemplo e configuração da biblioteca JPA

Crie um projeto Java e em seguida crie o diretório META-INF na pasta raiz. Esta pasta será responsável por hospedar o arquivo de configuração da JPA, o persistence.xml.

Antes de criar este arquivo, faça o download dos JARs do Hibernate com JPA e o conector MYSQL. Feito o download, acrescente-os no build path do projeto.

O persistence.xml é responsável por definir os parâmetros de funcionamento da JPA e deve conter informações como: nome da unidade de persistência, string de conexão com o bando de dados, usuário, senha, nome do banco, driver de conexão, entre outros. Como o objetivo do artigo não é esmiuçar este arquivo, segue abaixo o modelo básico para completar este tutorial.

2. Criando a classe de conexão com o banco de dados

Agora crie a classe que será responsável por estabelecer a conexão com o banco de dados utilizado pelo sistema.

Por ser um recurso caro para a aplicação, o atributo factory será utilizado com o modificador non-access static, ou seja, este atributo será único para toda instância produzida da classe ConnectionFactory. Desta forma, a factory será criada apenas na primeira vez em que a classe ConnectionFactory for utilizada.

3. Criação das classes de modelo

As classes modelo indicam a representação de entidades que necessitam de integração com o banco de dados. Para isto, crie uma interface que estipula o método getId(). Este método será responsável por retornar a chave primária das entidades “clientes”, que por sua ver é do tipo Long. A interface EntidadeBase será o contrato das entidades da aplicação que precisam utilizar recursos do SGBD, ou seja, toda e qualquer entidade que trabalhe com persistência de dados deverá “assinar” este contrato. 

Sabendo que as entidades modelo terão o contrato de EntidadeBase assinado, é possível amarrar a especificação da classe DAO genérica para que ela aceite todo tipo de entidade que seja implemente esta interface e atenda aos requisito de IS-A EntidadeBase. Crie duas classes, Pessoa e Carro, implementando a interface EntidadeBase e especifique os atributos e comportamenos conforme abaixo:

As anotações @Entity, @Table, @Column, @Id, @GeneratedValue são recursos fornecidos pela JPA a fim de especificar que uma determinada classe seja identificada como uma classe modelo para manipulação de dados junto ao SGBD. Em palavras mais grosseiras, estas anotações fazem com que a classe represente uma entidade, tabela ou coluna no banco de dados.

4. Criação da classe DAO genérica

Agora que a aplicação é capaz de gerar uma conexão com o banco de dados e mapear as entidades de modelo, crie uma classe com o nome de DaoGenerico. Esta classe será responsável por centralizar as lógicas de persistência em comum entre o grupo de entidades do contrato EntidadeBase. Este artigo abordará os métodos para adicionar, modificar, pesquisar por chave primária e excluir um registro no banco de dados, o famoso CRUD.

Esta declaração de classe permite gerar uma instância de DaoGenerico representando qualquer entidade que assine o contrato EntidadeBase (T extends EntidadeBase). Em seguida, defina um atributo manager, do tipo EntityManager e com modificador non-access static. Este atributo permitirá a utilização dos métodos da JPA.

Para facilitar e simplificar o entendimento, o ciclo de vida do EntityManager não será gerenciado.

Um breve exemplo de como instanciar um objeto de DaoGenerico representando a entidade Pessoa.

A JPA possui um método capaz de realizar a busca se baseando pelo tipo de uma entidade modelo e sua chave primária, no caso o atributo ID das classes de modelo. A sintaxe desse método é:

Crie o método de busca, findById, recebendo como parâmetro o tipo da entidade modelo desejada e o ID da mesma. Além disto, será definido como retorno uma entidade do tipo T. Observe que T é a entidade modelo que contratou os serviços de EntidadeBase.

Os métodos de cadastrar e editar possuem um comportamento muito parecido, salvar algo no banco de dados. Sendo assim, é possível verificar se o argumento do método possui um ID nulo ou não. Caso a verificação seja verdadeira, a entidade será salva, caso contrário atualizada.

Para isto, o método saveOrUpdate será criado sem retorno, pois o objetivo no momento é apenas inserir/atualizar o registro no banco, e receberá como parâmetro uma entidade do tipo T. Imagine que a entidade modelo tenha várias outras entidades que são persistidas em cascata. Para garantir a integridade dos dados no banco, será aplicado o conceito de transação. Desta forma, se houver algum erro no processo de persistência em cascata, será feito o rollback de todo o processo envolvido neste contexto.

O método de exclusão funcionará um pouco diferente dos demais, pois se faz necessário realizar uma busca da entidade que será removida para somente então removê-la. Infelizmente este é um ciclo implementado pelo próprio método remove() da JPA. O método remove da classe genérica não terá retorno e receberá como parâmetro o tipo da classe modelo e a chave primária da mesma, no caso o ID. Em seguida, o método de busca que já está criado será acionado e retornará a entidade que será excluída.

5. Testando a classe DAO genérica

Para testar os recursos genéricos de persistência crie as classes InsertApplication, UpdateApplication, FindByIdApplication e RemoveApplication, cada uma representando uma funcionalidade de CRUD na implementação do método main().

Screenshot at 11-36-28

Screenshot at 11-37-31 Screenshot at 11-37-21

Screenshot at 11-45-09

Screenshot at 11-46-57

Você pode baixar o código fonte deste artigo no meu GitHub.

Até a próxima!

Artigo publicado originalmente em Blog Raphael Neves

AGRADEÇA AO AUTOR COMPARTILHE!

Raphael Oliveira Neves

Mais artigos deste autor »

Engenheiro de software com 6+ anos de experiência em projetos de arquitetura e desenvolvimento de sistemas em Java, certificado Oracle, aspirante à escritor nas horas vagas e entusiasta de novas tecnologias.


2 Comentários

Deixe seu comentário

Seu endereço de e-mail não será publicado. Campos com * são obrigatórios!

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">