quarta-feira, dezembro 07, 2011

Persistência - JPA: DAO Genérico

Olá Seres Java!

Dando continuidade ao post anterior, demonstrarei o emprego do padrão DAO para realizar a persistência por meio da especificação JPA. O uso do pattern DAO, além de prover um melhor design para aplicação, agregar princípios de boas práticas, propicia um melhor reaproveitamento de implementação.

Segundo o Core J2EE Patterns, um Data Access Object:
Access to data varies depending on the source of the data. Access to persistent storage, such as to a database, varies greatly depending on the type of storage (relational databases, object-oriented databases, flat files, and so forth) and the vendor implementation.
Desta forma, entende-se que emprego de um DAO na aplicação propicia um ponto central, para acesso a dados, fazendo uso de um quantitativo de classes relativamente pequenos.

Tomando como base o projeto implementado no post anterior, será acrescida a estrutura de persistência por meio do DAO Genérico, assim, segue-se:

Criação da Interface DAO, esta deverá listar os métodos comuns a serem implementados por todos os DAOs:

Interface DAO:
package br.com.serjava.persistencia.dao;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;

public interface DAO<T, I extends Serializable> {

 public T save(T entity);
 
 public void remove(T entity);
 
 public T getById(Class<T> classe, I pk);
 
 public List<T> getAll(Class<T> classe);
 
 public EntityManager getEntityManager();
}


Uma vez concluído a interface DAO, criaremos uma classe abstrata (ou seja, só poderá ser extendida por outra, e não instanciada) denominada DAOImpl, esta conterá a implementação genérica dos métodos comuns:


DAOImpl:
package br.com.serjava.persistencia.dao.impl;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

import br.com.serjava.persistencia.dao.DAO;
import br.com.serjava.persistencia.util.Conexao;

public abstract class DAOImpl<T, I extends Serializable> implements DAO<T, I>{

 private Conexao conexao;
 
 @Override
 public T save(T entity) {
  
  T saved = null;
  
  getEntityManager().getTransaction().begin();
  saved = getEntityManager().merge(entity);
  getEntityManager().getTransaction().commit();
  
  return saved;
 }

 @Override
 public void remove(T entity) {
  getEntityManager().getTransaction().begin();
  getEntityManager().remove(entity);
  getEntityManager().getTransaction().commit();
  
 }

 @Override
 public T getById(Class<T> classe, I pk) {
  
  try {
   return getEntityManager().find(classe, pk);
  } catch (NoResultException e) {
   return null;
  }
  
 }

 @SuppressWarnings("unchecked")
 @Override
 public List<T> getAll(Class<T> classe) {
  
  return getEntityManager().createQuery("select o from " + classe.getSimpleName() + " o").getResultList();
 }

 @Override
 public EntityManager getEntityManager() {
  
  if (conexao == null) {
   conexao = new Conexao();
  }
  return conexao.getEntityManager();
 }

}


Uma vez criada a implementação base, resta-nos criar a interface que descreverá os métodos de um DAO referente a produto, que é a nossa entidade mapeada pelo JPA, assim temos que:


ProdutoDAO:
package br.com.serjava.persistencia.dao;

import br.com.serjava.persistencia.entity.Produto;

public interface ProdutoDAO extends DAO<Produto, Integer> {

}

Observem que a interface para Produto, é um DAO, como pode ser observado, esta extends a interface DAO padrão. Neste momento a estrutura genérica criada é tipada, quando informou-se: [...] extends DAO<Produto..., em outras palavras, estamos dizendo ao compilador da JVM que os métodos básicos herdados do DAO por ProdutoDAO, aceitarão exclusivamente objetos de Produto, desta forma, em tempo de compilação, se acidentalmente um desenvolvedor passar como parâmetro um objeto que não seja de Produto, será lançado um erro de compilação.

E, finalmente, tem-se a implementação de ProdutoDAO:


ProdutoDAOImpl:
package br.com.serjava.persistencia.dao.impl;

import br.com.serjava.persistencia.dao.ProdutoDAO;
import br.com.serjava.persistencia.entity.Produto;

public class ProdutoDAOImpl extends DAOImpl<Produto, Integer> implements ProdutoDAO {

}

Feito isto, temos nossa estrutura de persistência genérica, e sua implementação para persistirmos um objeto de Produto, na classe Main, criada no post anterior, demonstrarei o processo para se salvar e listar os objetos salvos:

public class Main {

 public static void main(String... args) {
  
  
  Produto produto = new Produto();
  produto.setNomeProduto("produto");
  produto.setQuantidade(22);
  produto.setValor(33.99);
  
  ProdutoDAO produtoDAO = new ProdutoDAOImpl();
  
  //salva um produto
  produtoDAO.save(produto);
  
  List<Produto> listaProdutosCadastrados = produtoDAO.getAll(Produto.class);
  
  for (Produto p : listaProdutosCadastrados) {
   System.out.println(p.getNomeProduto());
  }

 }
}


Aconselho, utilizando-se o mesmo princípio, testarem os demais métodos genéricos por meio de produto (getById, remove).

Para uma melhor compreensão da estrutura do projeto, segue sua estrutura e divisão de pacotes:

Bom, por este post é isto! Espero que tenham gostado e visualizado a flexibilidade que uma implementação genérica propicia!

28 comentários:

  1. Ficou meio estranho. A utilização do generico não era para evitar a criação excessiva de DAO ?
    Porque voce não deixa somente A implementação genérica ??

    ResponderExcluir
    Respostas
    1. Olá Onezino, obrigado pelo comentário.

      Sem dúvida é uma abordagem. Mas prevendo a necessidade de queries específicas prefiro já estruturar desta forma.

      Como deve ter observado, o DAOImpl é uma classe abstrata, para que nesta abordagem sempre tenha que ter o DAO específico, faço uso da estrutura genérica por meio do polimorfismo mesmo.

      Logicamente, seria possível obter o entityManager numa outra camada, para implementações específicas, mas na minha concepção não é adequado, pois estaria fazendo uma implementação de AccessObject numa camada não específica a isto. Há também a abordagem de se criar o DAO específico apenas quando houver, de fato, a especificidade. Neste caso, acho estranho invocar um DAO ora por seu específico, ora pela generalização, assim por questões de padronização prefiro sempre pelo seu específico.

      Enfim... como sabe, arquitetura é sempre plausível de discussão, não existe a melhor, mas sim, a mais adequada a cada visão e necessidade. Particularmente, acho mais adequado desta forma.

      Obrigado pela visita e o comentário! Volte sempre que puder e comente!

      []s

      Excluir
  2. Olá amigo

    Tem como disponibilizar os seus fontes??

    ResponderExcluir
  3. Cara, eu estava quebrando minha cabeça pra resolver isso...
    tava errando na parte da herança

    ResponderExcluir
  4. Este comentário foi removido pelo autor.

    ResponderExcluir
  5. Olá amigo, neste cabeçalho de classe
    ProdutoDAOImpl extends DAOImpl implements ProdutoDAO {}

    Não intendi a parte do Integer ...

    grande post, abraços.

    ResponderExcluir
    Respostas
    1. Olá!
      A parte do Integer, refere-se ao tipo numérico definido na PK.

      Excluir
  6. Olá meu amigo, pode postar o conteudo da classe Conexao?

    ResponderExcluir
    Respostas
    1. Olá! Obrigado pela visita!

      A Conexão ficou no post anterior, este foi uma melhoria, dá uma olhada aqui: http://serjava.blogspot.com.br/2011/12/persistencia-jpa-primeiros-passos.html

      []s, volte sempre :)

      Excluir
  7. Bom dia Fábio, parabéns pelo post. Só me responde uma coisa, você não fecha a entityManager, se sim, onde?

    ResponderExcluir
    Respostas
    1. Olá obrigado pela visita e comentário!

      Então, não fecho o entityManager, e nem recomendo... pode soar estranho, mas o processo de criar o entityManager e, geralmente, esta associado a criar uma conexao do Bd, é algo que gera certo custo.

      Se você deixa o recurso criado, o próximo que usar não terá que esperar o delay de criação...

      Em aplicações JEE ou mesmo com Spring, o entityManager é "criado" junto com o contexto de aplicação, ou seja, quando sua aplicação sobe no servidor, em termos superficiais.

      Logo, o container gerencia isto pra você, no caso de EJB's, até um pool é criado, justamente pra minimizar custo de processamento (abertura e fechamento de conexao) e prover o reaproveitamento de instâncias.

      Qualquer dúvida, fique a vontade para replicar!

      Volte sempre []s

      Excluir
    2. Mas que é realmente demora é a criação da EntityManagerFactory, que realmente deve ser criada apenas uma vez. Mas a EntityManager tem que ser fechada no fim das operações de CRUD, pelo menos foi o que eu li em outros blogs e livros.
      Eu to perguntando isso, pq tenho probleamas com lazy initialization exception e não sei onde devo fechar a entityManager.
      Já implantei o filtro OpenSessionInViewFilter, mas nada.

      Excluir
    3. Particularmente, numa vi fechar o entityManager quando se usa Spring ou EJB, o container gerencia.
      LazyInitializarion acontece quando você está tentando acessar uma lista, por exemplo:
      venda.getListaProdutos();

      Se você estiver fora do contexto de persistência será lançado o LazyInitializationException.

      Já ao se fazer esta chamado num método dentro de um contexto transacional, seja do spring ou ejb, certamente você não terá esta exceção.

      O que você está usando?

      Excluir
    4. Mas se você está criando manualmente, sem Spring ou EJB como gerenciadores, aí sim é melhor fechar para não ter muitas instâncias.

      Ou... pode criar um Singleton da conexão.

      Excluir
    5. Não estou usando o Spring, estou criando meus DAOs exatamente como está no seu post, só que fechando a entityManager no fim de cada metodo (é bem aí que eu to me perdendo, pq não quero ter varias conexões abertas, mas se eu fecha-la acontece a LazyInitializationException e não quero mudar meus relacionamentos para EAGER).

      Para criar a EntityManagerFactory eu uso o padrão singleton para garantir que só um instancia da EntityManagerFactory seja criada.

      Excluir
    6. Em que ponto da sua aplicação você tenta utilizar a lista (relacionamento do JPA) e obtém a exceção?

      Excluir
    7. tratasse de uma aplicação web, no momento da renderização da pagina, quando o datatable da jsf tenha exibir os atributos da entidade que possuem relacionamento LAZY

      Excluir
    8. Certo, mas quando você busca os dados por meio da consulta, vocë faz em uma classe da sua aplicação, correto?

      Excluir
  8. Fala Fabio, Parabéns pelo tutorial!

    não consegui ver o código do pacote
    br.com.serjava.persistencia.util.Conexao, tem como você postar ai?

    obrigado!!!

    ResponderExcluir
    Respostas
    1. Olá Daniel, obrigado pela visita!

      Está no primeiro post: http://serjava.blogspot.com.br/2011/12/persistencia-jpa-primeiros-passos.html

      Se você estiver usando uma app web há mecanismos melhores para gerenciar a conexão pra você.

      Qualquer dúvida poste aí.
      Volte sempre!

      Excluir
  9. Fábio, para aplicações web, como ficaria o seu código. Usando as anotações do EJB?

    ResponderExcluir
    Respostas
    1. Olá, desculpe a demora pelo retorno, e obrigado pela visita.

      Como o intuito de EJBs em si são componentes, e estes quando o servidor sobre são instanciados para o pool, logo consomem recursos; eu aconselho a ter apenas um DAO como EJB... na verdade, esse DAO segue o pattern factory.

      Ou seja, apenas o seu DAOFactory se torna um EJB e este, retorna a instancia dos DAOs específicos que você precisa.

      Deu pra entender?

      []s

      Excluir
  10. Gostei da abordagem. Principalmente porque tenho metodos especificos para alguns DAOs.

    ResponderExcluir
  11. Olá Fabio! O Post é antigo, mas queria uma orientação. No meu caso a conexão é criada no Filter:

    @WebFilter(servletNames = { "Faces Servlet" })
    public class JPAFilter implements Filter {

    private EntityManagerFactory factory;

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    this.factory = Persistence.createEntityManagerFactory("EM-MS");
    }
    ...
    }

    Eu recupero esta conexão de um contexto web:

    public EntityManager getEntityManager() {
    FacesContext fc = FacesContext.getCurrentInstance();
    ExternalContext ec = fc.getExternalContext();
    HttpServletRequest request = (HttpServletRequest) ec.getRequest(); EntityManager manager = (EntityManager)request.getAttribute("EntityManager");
    return manager;
    }

    Como eu passaria a conexão, o entityManager para o a ProdutoDaoImpl. Poderia criar um construtor na classe abstrata DAOImp ?

    Obrigado pela atenção


    ResponderExcluir
    Respostas
    1. Olá obrigado pela Visita,

      Diferente esta sua abordagem pelo contexto do FacesContext, não vi ainda desta forma.

      Mas vamos lá, da forma que você sugeriu, via construtor daria certo; mas pensei no pattern factory, ai você poderia criar, um DAOFactory que retorna a instância do DAO que você necessita e atribui o entityManager a cada um deles.

      Acho que ficaria bacana em termos estruturais a depender, claro, da aplicação e tal.

      Espero ter ajudado.

      []s

      Excluir
    2. Obrigado Fábio! Vou pesquisar este pattern.

      Excluir