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!