quarta-feira, 8 de junho de 2011

Java + JPA + Hibernate + DAO Genérico

Olá, hoje vou mostrar como fazer um DAO Genérico no Java (contendo SaveOrUpdate, Remove, FindAll, FindByID) que pode ser usado por todos os DAOs. Facilitando a criação de novos projetos, podendo ser usado com a integração com o Adobe Flex através do RemoteObject.

Isto é para quem não quer usar nenhuma espécie de Framework (ex: Spring, que faz isso de uma forma mais genérica).

Primeiramente criei minha classe contendo todos os métodos (save, update, delete, find), para minha classe ficar "genérica" ao invés de receber as entidades específicas, recebe Object (pode passar as Entidades que o java consegue reconhecer). Após criar esta classe contendo os métodos, é só criar os DAOs extendendo da classe genérica, com isso já terá todos os métodos implementados. Podendo fazer validações específicas para cada DAO.

Vamos a prática:

Este é minha classe genérica contendo todos os métodos.
GenericDAO.java
package com.blogspot.desenvolvendoemflex.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;

import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

/**
 * @author SAMUEL FACCHINELLO
 * @version 1.0
 */
public class GenericDAO {

 /**
  * Salva ou Atualiza dependendo se já existe ou não o objeto no banco de dados. 
  * @param o: Entidade a ser Salva (se ainda não existir) ou a ser Atualizada (se já existir)
  * @return Retorna a Entidade passada como parâmetro.
  * @throws Exception
  */
 public Object saveOrUpdate(Object o) throws Exception {
  Session em = HibernateSessionFactory.getSession();
  Transaction trans = em.beginTransaction();
  try {
   trans.begin();
   em.merge(o);
   trans.commit();
   return o;
  } catch (Exception e) {
   if (trans.isActive()) {
    trans.rollback();
   }
   e.printStackTrace();
   throw e;
  } finally {
   em.close();
  }
 }

 /**
  * Salva ou Atualiza dependendo se já existe ou não o objeto no banco de dados. 
  * @param list: lista das Entidades a serem Salvas (se ainda não existir) ou a serem Atualizadas (se já existir)
  * @return Retorna a Entidade passada como parâmetro.
  * @throws Exception
  */
 public void saveOrUpdateAll(ArrayList<Object> list) throws Exception {
  Session em = HibernateSessionFactory.getSession();
  Transaction trans = em.beginTransaction();
  try {
   trans.begin();
   for (Object o : list) {
    em.merge(o);
   }
   trans.commit();
  } catch (Exception e) {
   if (trans.isActive()) {
    trans.rollback();
   }
   e.printStackTrace();
   throw e;
  } finally {
   em.close();
  }
 }

 /**
  * Remove a Entidade do banco de dados.
  * @param o: Entidade a ser removido do banco de dados.
  * @throws Exception
  */
 public void remove(Object o) throws Exception {
  Session em = HibernateSessionFactory.getSession();
  Transaction trans = em.beginTransaction();
  try {
   trans.begin();
   em.delete(o);
   trans.commit();
  } catch (Exception e) {
   if (trans.isActive()) {
    trans.rollback();
   }
   throw e;
  } finally {
   em.close();
  }
 }

 /**
  * Remove a Entidade do banco de dados.
  * @param list: Lista de Entidades a serem removidas do banco de dados.
  * @throws Exception
  */
 public void removeAll(ArrayList<Object> list) throws Exception {
  Session em = HibernateSessionFactory.getSession();
  Transaction trans = em.beginTransaction();
  try {
   trans.begin();
   for (Object o : list) {
    em.delete(o);
   }
   trans.commit();
  } catch (Exception e) {
   if (trans.isActive()) {
    trans.rollback();
   }
   throw e;
  } finally {
   em.close();
  }
 }

 /**
  * Retorna a lista de itens salvo no banco de dados de acordo com a Entidade passada como parâmetro.
  * @param classe : Entidade que deverá buscar no banco de dados.
  * @return List contendo todos os objetos encontrados da entidade passada como parâmetro.
  */
 @SuppressWarnings("unchecked")
 public static List findAll(Class classe) {
  Session em = HibernateSessionFactory.getSession();
  Query query = em.createQuery("Select a from " + classe.getName() + " a");
  return query.list();
 }

 /**
  * Faz a busca no banco pelo ID da entidade
  * @param classe : Entidade a ser buscada
  * @param id : valor a ser procurado
  * @return Objeto encontrado no banco, caso não encontre será retornado NULL
  * @throws Exception
  */
 @SuppressWarnings("unchecked")
 public static Object findById(Class classe, Object id) throws Exception {
  if (classe == null) {
   throw new Exception("classe não pode ser nula.");
  }

  if (id == null) {
   throw new Exception("id não pode ser nulo.");
  }

  Field[] fields = classe.getDeclaredFields();
  String chave = "";
  for (Field field : fields) {
   if (field.getAnnotation(Id.class) != null) {
    chave = field.getName();
    break;
   }
  }

  if (chave.equals("")) {
   throw new Exception("Annotation @Id não encontrada na classe " + classe.getName());
  }

  Session em = HibernateSessionFactory.getSession();
  Query query = em.createQuery("Select a from " + classe.getName() + " a" +
          " where a." + chave + " = :id").setParameter("id", id);
  query.setMaxResults(1);
  return query.uniqueResult();
 }

 /**
  * Retorna objeto de acordo com a UniqueConstraint definida na Entidade
  * @param classe : Entidade a ser buscada
  * @param ids : Array contendo os IDS a serem buscados (na mesma ordem em que a UniqueConstraint foi definido)
  * @return Objeto encontrado no banco, caso não encontre será retornado NULL 
  * @throws Exception
  */
 @SuppressWarnings("unchecked")
 public static Object findByUniqueConstraints(Class classe, ArrayList<Object> ids) throws Exception {
  if (classe == null) {
   throw new Exception("classe não pode ser nula.");
  }

  if (ids == null || ids.size() == 0) {
   throw new Exception("ids não pode ser nulo.");
  }

  ArrayList<String> chaves = new ArrayList<String>();
  ArrayList<Class> tipos = new ArrayList<Class>();
  Annotation annotation = classe.getAnnotation(Table.class);
  if (annotation != null) {
   Table table = (Table) annotation;

   for (UniqueConstraint uniqueConstraint : table.uniqueConstraints()) {
    for (String column : uniqueConstraint.columnNames()) {
     tipos.add(classe.getDeclaredField(column).getType());
     chaves.add(column);
    }
   }
  } else {
   throw new Exception("Annotation @Table não encontrada na classe " + classe.getName());
  }

  if (chaves.size() == 0) {
   throw new Exception("UniqueConstraints não encontrada na annotation @Table da classe " + classe.getName());
  }

  if (chaves.size() != ids.size()) {
   throw new Exception("O número de parâmetros passados não confere com o número de UniqueConstraints encontrados na classe " + classe.getName() + ". " + ids.size() + " foram passados, mas " + chaves.size() + " eram esparados.");
  }

  String str = "";
  for (int i = 0; i < chaves.size(); i++) {
   if (!ids.get(i).getClass().equals(tipos.get(i))) {
    throw new Exception("Tipos de dados incompatíveis. O parâmetro número " + i + " é incompatível, foi passado:" + ids.get(i).getClass().toString() + " e estava esperando: " + tipos.get(i).toString());
   }
   if (str.length() == 0) {
    str = " where a." + chaves.get(i) + " = " + ids.get(i);
   } else {
    str += " and " + chaves.get(i) + " = " + ids.get(i);
   }
  }

  Session em = HibernateSessionFactory.getSession();
  Query query = em.createQuery("Select a from " + classe.getName() + " a " + str);
  query.setMaxResults(1);
  return query.uniqueResult();
 }
}


Aqui esta meu DAO com uma validação:

BancosDAO.java
package com.blogspot.desenvolvendoemflex.dao;

import com.blogspot.desenvolvendoemflex.entity.Bancos;
import com.blogspot.desenvolvendoemflex.util.GenericDAO;

public class BancosDAO extends GenericDAO {
 public Object saveOrUpdate(Bancos o) throws Exception {
  //TODO: aqui é possivel fazer algumas validações
  if (o.getDescricao() == null || o.getDescricao().equals("")) {
   throw new Exception("Descrição é obrigatório.");
  }
  //TODO: chamo a classe genérica passando por parâmetro minha Entidade "Banco".
  return super.saveOrUpdate(o);
 }
}

A Entidade, contendo as Annotations @Entity e @Table (com @UniqueConstraint para teste do findByUniqueConstraints)
Bancos.java
package com.blogspot.desenvolvendoemflex.entity;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
/*TODO: uniqueConstraints usado para exemplo do FIND, neste caso ele não era necessário*/
@Table(uniqueConstraints = { @UniqueConstraint(columnNames = "cod") })
public class Bancos {

 @Id
 private Integer cod;

 private String descricao;

 public Integer getCod() {
  return cod;
 }

 public void setCod(Integer cod) {
  this.cod = cod;
 }

 public String getDescricao() {
  return descricao;
 }

 public void setDescricao(String descricao) {
  this.descricao = descricao;
 }

}


Para testar, basta chamar o DAO de qualquer lugar, passando os parâmetros necessários em qualquer um dos métodos do GenericDao.

Exemplo de utilização:
  BancosDAO bancosDAO = new BancosDAO();
  Bancos bancos = new Bancos();
  bancos.setCod(1);
  bancos.setDescricao("Banco 1");

  bancosDAO.saveOrUpdate(bancos);

  bancos.setCod(2);
  bancos.setDescricao("Banco 2");
  bancosDAO.saveOrUpdate(bancos);

  ArrayList<Bancos> allBancos = (ArrayList<Bancos>) BancosDAO.findAll(Bancos.class);

  for (Bancos banco : allBancos) {
   System.out.println(banco.getDescricao());
  }

  bancosDAO.remove(BancosDAO.findById(Bancos.class, 2));

  BancosDAO.findAll(Bancos.class);
  
  /*TODO: Passando um array contendo a UniqueConstraint para pesquisa no banco */
  ArrayList<Object> ids = new ArrayList<Object>();
  ids.add(1);
  BancosDAO.findByUniqueConstraints(Bancos.class, ids);
  
  /*TODO: tratando o erro, Descrição é obrigatório */
  bancos.setCod(2);
  bancos.setDescricao("");
  bancosDAO.saveOrUpdate(bancos);


Espero que tenha ajudado.

Obrigado.

Deixe um comentário.

Nenhum comentário:

Postar um comentário