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!

terça-feira, dezembro 06, 2011

Persistência - JPA: Primeiros passos

Olá pessoAll!

Neste tópico abordarei a configuração e os primeiros passos para se usar a persistência de dados com um framework Objeto-Relacional (ORM).

Observo que muitas pessoas, principalmente iniciantes em desenvolvimento, têm grande interesse por tal aprendizado, porém, num primeiro momento encontram dificuldades que realmente tornam-se impactantes ao estudo e, consequentemente, ao aprendizado.

CONTEXTO
A grande motivação para o uso de um framework ORM em uma aplicação deu-se, mediante ao seguinte paradigma: de um lado encontra-se o banco de dados no SGBD (calcado suas bases na álgebra e o cálculo relacional) e de outro, o modelo da aplicação orientado a objetos, onde, ambos, representam, e uma forma ou de outra, a estrutura da aplicação. Neste sentido, deveria ser fácil realizar tal conversão: OO to Relational.

Embora, em tese, seria fácil tal conversão, verificou-se significativo trabalho e custo (tempo) para transformar um Modelo Orientado a Objetos, por meio e seu DAO (Data Access Object), utilizando-se JDBC.

Visando atender a esta necessidade, bem como, prover maior produtividade no desenvolvimento, surgiram-se várias propostas para a realização do mapeamento objeto-relacional, tais como: Hibernate, TopLink, EclipseLink, e outros. Neste cenário, cada proposta, evidentemente, trazia consigo suas especificidades no quesito implementação.

No intuito de se padronizar a implementação de tecnologias ORM, surgiu a JPA (Java Persistence API), que especificou o uso de ferramentas ORM. Em teoria, qualquer framework que implemente o padrão JPA deve funcionar igualmente.

MÃO NA MASSA
Será utilizado o SGBD PostgreSql para a a implementação.
Baseado no DER da tabela produto, exibido pela Figura 1:
Tem-se o script de criação da tabela, no SGBD:
CREATE TABLE produto
(
  id_produto serial NOT NULL,
  nm_produto character varying(45),
  quantidade integer,
  valor numeric,
  primary key produto_pkey PRIMARY KEY (id_produto)
)

Criaremos um projeto intitulado como Persistencia, do tipo Java Project no Eclipse. Posteriormente, devemos acrescer a característica de um projeto JPA, para tanto, clique com o botão direito sobre o projeto criado, e selecione a opção Properties.
Em seguida, seleciona na barra lateral, a opção Project Facets, e clique no link Convert do Faceted Form, e marque a opção JPA, conforme a Figura 2:

Observe que logo abaixo do quadro de opções, apareceu um alerta, "further configuration required", isto acontece pois o Eclipse não contém os artefatos necessários para serem adicionados ao classpath da aplicação. Para resolvermos isto, e colocarmos manualmente todos os artefados, clique sobre o link desta mensagem, e no combo Type, selecione Disable Library Configuration, conforme Figura 3:


Em seguida, clique em Ok, e o projeto ja deverá estar pronto para procedemos a configuração do JPA. Devemos, num primeiro momento, adicionarmos os artefatos necessários a isto, assim, na mesma hierarquia do diretório src do projeto, criaremos o diretório libs.

Tendo criado o diretório, adicionaremos os artefatos necessários, identifique-os na Figura 4:


Para a obtenção destes artefatos, acesse o sites: http://commons.apache.org/http://www.hibernate.org/ , http://www.slf4j.org/http://jdbc.postgresql.org/, ou então, como uma simples busca pela nomenclatura de cada arquivo.

Uma vez adicionados os artefatos necessários, resta referenciá-los aos classpath da aplicação, para isso, selecione todos eles, clique com o botão direito sobre, e siga as opções: Build Path --> Add to build path. Pronto, todas as dependências estão devidamente adicionadas e referenciadas.

O arquivo persistence.xml, contido em META-INF, dentro de src, deverá conter os dados necessários para conexão, devemos parametrizá-lo do seguinte modo:

 
 
  org.hibernate.ejb.HibernatePersistence
  
  
   
   
   
   
  
 
 


Observe que os dados de acesso ao banco foram parametrizados, tais como, url para acesso, usuário e senha (atenção, estes dados podem variar no seu ambiente).

CONEXÃO E MAPEAMENTO

A conexão com o banco de dados é gerida pelo EntityManager, da própria especificação Java Persistence API, este contém os métodos necessários para que o ORM realize suas operações de forma adequada.

Devemos criar um objeto Java que representará a estrutura da tabela produto que criamos no banco. Nesta classe, efetuaremos o mapeamento de modo que o JPA consiga entender o que o objeto e seus atributos representam no modelo relacional, assim segue-se:
package br.com.serjava.persistencia.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="produto")
@SequenceGenerator(name="produto_id_produto_seq", sequenceName="produto_id_produto_seq", allocationSize=1)
public class Produto implements Serializable {

 @Id
 @GeneratedValue(generator="produto_id_produto_seq", strategy=GenerationType.SEQUENCE)
 @Column(name="id_produto")
 private Integer idProduto;
 
 @Column(name="nm_produto")
 private String nomeProduto;
 
 @Column(name="quantidade")
 private Integer quantidade;
 
 @Column(name="valor")
 private Double valor;
        //getters and setters



Criaremos agora a classe de conexão, que deverá gerar o EntityManager, portanto:
public class Conexao {

 //nome da unidade de persistencia definia no persistence.xml
 private static final String UNIT_NAME = "PersistenciaPU";
 
 private EntityManagerFactory emf = null;
 
 private EntityManager em = null;
 
 public EntityManager getEntityManager() {
  
  if (emf == null) {
   emf = Persistence.createEntityManagerFactory(UNIT_NAME);
  }
  
  if (em == null) {
   em = emf.createEntityManager();
  }
  
  return em;
 }

Demonstrarei, como persistir um objeto de produto gerido pelo contexto de persistência do JPA, em posts futuros, abordarei as demais possibilidades e operações do JPA. Assim, necessitamos obter o EntityManager, e por ele, persistir o objeto Produto, que foi devidamente mapeado:
public class Main {

 public static void main(String... args) {
  
  Produto produto = new Produto();
  produto.setNomeProduto("produto");
  produto.setQuantidade(22);
  produto.setValor(33.99);
  
  EntityManager em = new Conexao().getEntityManager();
  
  em.getTransaction().begin();
  em.persist(produto);
  em.getTransaction().commit();
 }
}

Execute a classe Main, e verifique no banco que deverá haver um registro com as informações atribuidas ao objeto Produto.

Espero que tenham gostado, tentei resumir um vasto e detalhado processo da forma mais simples e objetiva possível, espero ter atendido as expectativas.
Até breve!