viernes, 19 de julio de 2013

DAO genérico para JPA (ORM)

Si usamos un ORM (Object-Relational Mapping) para la capa de persistencia en una base de datos relacional de nuestra aplicación ya sea Hibernate o JPA probablemente después de desarrollar unos cuantos servicios DAO nos daremos cuenta que tenemos unas cuantas operaciones básicas muy similares en todos.

Si queremos evitar tener duplicado ese código y ahorrarnos la codificación de esas operaciones básicas podemos desarrollar un DAO genérico que nos sirva para todas las entidades persistentes de nuestra aplicación usando los generics del lenguaje Java. Las operaciones candidatas a incluir en este DAO son: búsqueda por id, persistencia de un objeto, borrado, actualización, búsqueda paginada de todos los objetos y número de entidades persistidas de un tipo.

El código que podemos usar puede ser el siguiente:

package es.com.blogspot.elblogdepicodev.tapestry.jpa.dao;
import java.util.List;
import org.apache.tapestry5.jpa.annotations.CommitAfter;
import es.com.blogspot.elblogdepicodev.tapestry.jpa.misc.Pagination;
public interface GenericDAO<T> {
T findById(Long id);
List<T> findAll();
List<T> findAll(Pagination paginacion);
long countAll();
@CommitAfter
void persist(T producto);
@CommitAfter
void remove(T producto);
@CommitAfter
void removeAll();
}
view raw GenericDAO.java hosted with ❤ by GitHub
package es.com.blogspot.elblogdepicodev.tapestry.jpa.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import es.com.blogspot.elblogdepicodev.tapestry.jpa.misc.Pagination;
public class GenericDAOImpl<T> implements GenericDAO<T> {
private Class<T> clazz;
private EntityManager entityManager;
public GenericDAOImpl(Class<T> clazz, EntityManager entityManager) {
this.clazz = clazz;
this.entityManager = entityManager;
}
@Override
public T findById(Long id) {
return entityManager.find(clazz, id);
}
@Override
public List<T> findAll() {
return findAll(null);
}
@Override
public List<T> findAll(Pagination paginacion) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(clazz);
Root<T> root = cq.from(clazz);
cq.select(root);
if (paginacion != null) {
cq.orderBy(paginacion.getOrders(root, cb));
}
Query q = entityManager.createQuery(cq);
if (paginacion != null) {
q.setFirstResult(paginacion.getStart());
q.setMaxResults(paginacion.getEnd() - paginacion.getStart() + 1);
}
return q.getResultList();
}
@Override
public long countAll() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
cq.select(cb.count(cq.from(clazz)));
return entityManager.createQuery(cq).getSingleResult().intValue();
}
@Override
public void persist(T object) {
entityManager.persist(object);
}
@Override
public void remove(T object) {
entityManager.remove(object);
}
@Override
public void removeAll() {
String jpql = String.format("delete from %s", clazz.getName());
Query query = entityManager.createQuery(jpql);
query.executeUpdate();
}
}
Por supuesto, este DAO es muy simple pero podría ser ampliado para permitir hacer búsquedas además de sobre todos los elementos de una tabla y con paginación con unos determinados criterios de búsqueda que nos cubran las mayoría de los casos que necesitemos, es decir, podríamos implementar un método tal que le pasemos un objeto que se encargue construir lo que serían las cláusulas específicas de la consulta: findByCriteria(SimpleCriteriaBuilder criteria, Pagination pagination), findByNamedQuery(String namedQuery) o findByQuery(String query).

package es.com.blogspot.elblogdepicodev.tapestry.jpa.dao;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
public interface SimpleCriteriaBuilder<T> {
void build(CriteriaBuilder cb, CriteriaQuery<T> cq, Root<T> c);
}
Referencia:
Código fuente completo del ejemplo
Persistencia con JPA y Apache Tapestry