¿Cómo realizar un DAO sin repetir con Generics?

El problema

Uno de los problemas recurrentes al momento de utilizar Hibernate es la creación de los DAOs (Data Access Objects) que nos permitan persistir nuestros objetos de negocio.

Hibernate?
Gracias a Hibernate realizar la compleja tarea de recuperar y guardar objetos en una base de datos relacional es cosa del pasado.

Vamos a ver un ejemplo:

Supongamos que tengo una clase "Alumno", para persistir instancias del mismo con Hibernate deberíamos escribir las siguientes lineas de código:

public void save(Alumno alumno) {
	Session session = HibernateSessionFactory.getSessionFactory()
 		.getCurrentSession();
       try {
		session.beginTransaction();
		session.save(alumno);
		session.getTransaction().commit();
	} catch (HibernateException ex) {
		ex.printStackTrace();
	}
}

Así, con estas instrucciones Hibernate guardará el objeto alumno, creando su correspondiente registro en la tabla que hayamos asignado para el objeto tipo "Alumno".

En este caso estas lineas de código se repetirán cada vez que tengamos que persistir una clase de negocio, esto en muchos casos podría ser varias decenas.

Solución


NOTA: Esta solución está basada en una idea original de Nilesh Kapadia alojada en su sitio: Hibernate DAO using Java 5 Generics. La principal diferencia es que el se basa en el framework Spring, y yo propongo esta solución sin necesidad de Spring.

Java 5 introdujo el concepto de Generics (Genéricos) el cual nos permite especificar el tipo de dato final a utilizar (con esta técnica es posible, por ejemplo, utilizar los Collections de Java y realizar un chequeo extra al momento de compilar del tipo de objeto almacenado en la misma) .

Veamos el ejemplo anterior con Generics:

public void save(T t) {
	Session session = HibernateSessionFactory.getSessionFactory()
		.getCurrentSession();
	try {
		session.beginTransaction();
		session.save(t);
		session.getTransaction().commit();
	} catch (HibernateException ex) {
		ex.printStackTrace();
	}
}

Oye! de donde ha salido "T"??
T es nuestro tipo de dato genérico, el mismo deberia estar definido en la declaración de nuestra clase:

public class GenericDAO<T> {

Para hacer uso de esta clase "genérica" lo que deberíamos hacer es extender esta clase para persistir nuestros objetos:

import ar.com.slemos.model.Alumno;
public class AlumnoDAO extends GenericDAO<alumno> {}

Listo! ya tenemos nuestro DAO para Alumno.

Conclusión

Creo que podemos decir que el uso de Generics en este caso nos beneficia al poder extender fácilmente los objetos DAO.

Les dejo a continuación el código de GenericDAO, para que puedan ver los métodos implementados:

package ar.com.slemos.dao;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import ar.com.slemos.exception.UnableToSaveException;
import ar.com.slemos.util.HibernateSessionFactory;
 
public abstract class GenericDAO<T> {
 
	public Class<T> domainClass = getDomainClass();
 
	private Session session;
 
	/**
	  * Method to return the class of the domain object
	  */
 
	protected Class getDomainClass() {
	 	if (domainClass == null) {
			ParameterizedType thisType = (ParameterizedType) getClass()
 				.getGenericSuperclass();
			domainClass = (Class) thisType.getActualTypeArguments()[0];
	 	}
		return domainClass;
	}
 
	@SuppressWarnings("unchecked")
	public T load(Long id) {
		T returnValue = (T) getHibernateTemplate().load(domainClass, id);
		session.getTransaction().commit();
		return returnValue;
	}
 
	public void update(T t) throws UnableToSaveException {
		try {
			getHibernateTemplate().update(t);
	 		session.getTransaction().commit();
	 	} catch (HibernateException e) {
	 		throw new UnableToSaveException(e);
		}
	}
 
	public void save(T t) throws UnableToSaveException {
	 	try {
	 		getHibernateTemplate().save(t);
	 		session.getTransaction().commit();
	 	} catch (HibernateException e) {
	 		throw new UnableToSaveException(e);
	 	}
	}
 
	public void delete(T t) {
	 	getHibernateTemplate().delete(t);
	 	session.getTransaction().commit();
	}
 
	@SuppressWarnings("unchecked")
	public List<T> getList() {
		List<T> returnList = getHibernateTemplate().createQuery(
	 			"from " + domainClass.getName() + " x").list();
		session.getTransaction().commit();
		return returnList;
	}
 
	public void deleteById(Long id) {
	 	Object obj = this.load(id);
	 	getHibernateTemplate().delete(obj);
	}
 
	public int deleteAll(boolean isSure) {
		int countDeleted = getHibernateTemplate().createQuery(
 			"delete " + domainClass.getName()).executeUpdate();
		if (isSure)
	 		session.getTransaction().commit();
	 	else
	 		session.getTransaction().rollback();
		return countDeleted;
	}
 
	public int count() {
	 	Integer count = (Integer) getHibernateTemplate().createQuery(
 			"select count(*) from " + domainClass.getName() + " x")
 			.uniqueResult();
		session.getTransaction().commit();
		return count.intValue();
	}
 
	public List<T> findByExample(T exampleObject) {
	 	Example example = Example.create(exampleObject).excludeZeroes()
 			.enableLike().ignoreCase();
		List<T> list = getHibernateTemplate().createCriteria(domainClass).add(
 			example).list();
		session.getTransaction().commit();
		return list;
	}
 
	private Session getHibernateTemplate() {
		session = HibernateSessionFactory.getSessionFactory()
 			.getCurrentSession();
		session.beginTransaction();
		return session;
	}
 
}

Les dejo también un zip con los fuentes para que puedan ver el ejemplo completo: [download id="12"]

Saludos!

Esta entrada fue publicada en Java y etiquetada , , . Guarda el enlace permanente.

7 respuestas a ¿Cómo realizar un DAO sin repetir con Generics?

  1. CArlos dijo:

    Como hago para hacerlo con NHibernate con C # aplicacion windows

  2. otiuqx dijo:

    Hola estoy empezando en el tema de Hibernate y me gustaria que me pases el zip con las fuentes del proyecto para asi estudiarlas. muchas gracias.

  3. maxi dijo:

    Sebastian, una pregunta, en cualquiera de los metodos que acceden a datos como Load o findByexample, etc…. al hacer

    session.getTransaction().commit();
    return list;

    estarias cerrando la session (y la transaccion) y creando el objeto resultado detached???

    me gustaria saber bien eso.

    Muchas gracias.

  4. Gustavo dijo:

    Gracias … excelente aporte…

  5. FELIPE dijo:

    oye muchas gracias, em ha sido de gran utilidad tu bolg, graaaaaaciasss muuuuuuuuuuuchas

  6. Antonio dijo:

    Me agrado este articulo!! muy buena informacion!! bueno ahora a hacer algunos experimentos jaja

    Saludos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>