Eventos en Hibernate (parte I)

0
22769

Eventos en Hibernate (Parte I)

Índice de contenidos

1. Introducción

2. Entorno

3. Preparando una
aplicación de ejemplo

3.1. Usando
Maven para crear el proyecto

3.2. Creando
el modelo

3.3. Creando
clases de prueba y probando la aplicación

4. Creando un oyente para escuchar

5. Dos mejor que uno

6. Conclusiones

1. Introducción

Este es el primero de una serie de tutoriales en los que vamos a hablar
del manejo de eventos en Hibernate. En este primer tutorial nos vamos a
centrar en los oyentes (listeners) que pueden utilizarse con una
SessionFactory para capturar los eventos que se producen al cargar una
entidad, guardarla, actualizarla, borrarla, etc. y realizar
algún tipo de acción en ese momento. En el
próximo tutorial veremos cómo asociar
métodos de retrollamada en nuestras entidades
a determinados eventos, de manera que éstos
métodos se ejecutarán al producirse los eventos
correspondientes. Esto útlimo además puede hacerse
por medio de anotaciones que siguen la especificación de EJB3.

2. Entorno

Para la realización de este tutorial se han utilizado
las siguientes herramientas:

  • Hardware: Portátil Asus G50Vseries (Core Duo
    P8600 2.4GHz, 4GB RAM, 320 GB HD).

  • Sistema operativo: Windows Vista Ultimate.

  • Eclipse Ganymede 3.4.1, con el plugin Q (http://code.google.com/p/q4e)
    para Maven

  • JDK 1.6.0

  • Maven 2.0.9

  • MySql 5.1.30

  • Hibernate 3

3. Preparando una
aplicación de ejemplo

3.1. Usando Maven para
crear el proyecto

Para mostrar el uso de los oyentes de eventos vamos a realizar
una sencilla aplicación Java que introduzca una serie de
usuarios en una base de datos y que después los recupere y los
muestre por la salida estándar.

Creamos un nuevo proyecto Maven, al que llamaremos
EventosHibernateI,
marcamos la casilla para crear un simple proyecto Java y pulsamos
finalizar.

Nuevo proyecto Maven

Editamos el fichero pom.xml para añadir las
dependencias necesarias para utilizar Hibernate y MySql.
Además, cambiamos el nivel de compilación por defecto
utilizado por Maven para poder utilizar anotaciones con Hibernate.


	4.0.0
	EventosHibernateI
	EventosHibernateI
	jar
	1.0-SNAPSHOT
	EventosHibernateI
	http://maven.apache.org

	
	
		
			
				org.apache.maven.plugins
				maven-compiler-plugin
				
					1.5
					1.5
				
			
		
	

	
		
		
			junit
			junit
			4.4
			test
		
		
		
		
			mysql
			mysql-connector-java
			5.0.5
		
		
		
		
			org.slf4j
			slf4j-log4j12
			1.5.2
			test
		
		
		
		
			org.hibernate
			hibernate-core
			3.3.1.GA
		
		
			org.hibernate
			hibernate-annotations
			3.4.0.GA
		
		
			javassist
			javassist
			3.8.1.GA
			runtime
		
	


3.2 Creando el modelo

Ahora crearemos nuestro modelo, consistente de una
única entidad Usuario, la cual
será manejada por Hibernate para persistir los usuarios en la
base de datos. La entidad tendrá los campos nombre,
apellidos, fecha de nacimiento y edad.

El campo edad se marca como Transient para que Hibernate no lo
guarde en la base de datos. Este campo será calculado mediante
la función «calcularEdad» en base al valor de la fecha de
nacimiento. Más adelante veremos como conseguir que
está función sea llamada automáticamente al
producirse el evento PostLoad, que ocurre justo después de la
carga de los datos de la entidad.

package modelo;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Transient;

@Entity
public class Usuario {

	@Id
	@GeneratedValue
	private Integer id;
	
	private String nombre;
	
	private String apellidos;
	
	private Date nacimiento;
	
	@Transient
	private int edad;

	public Usuario() {
		super();
	}

	public Usuario(String nombre, String apellidos) {
		this.nombre = nombre;
		this.apellidos = apellidos;
	}

	public void calcularEdad() {
		Calendar cumple = new GregorianCalendar();
		Calendar ahora = new GregorianCalendar();
		cumple.setTime(nacimiento);
		ahora.setTime(new Date()); // Comprobar que la fecha de nacimiento sea
									// anterior a la actual
		if (ahora.compareTo(cumple) < 0) { // Si no es anterior poner la edad a
											// 0
			edad = 0;
		} else {
			int ajuste = (ahora.get(Calendar.DAY_OF_YEAR)
					- cumple.get(Calendar.DAY_OF_YEAR) < 0) ? -1 : 0;
			edad = ahora.get(Calendar.YEAR) - cumple.get(Calendar.YEAR)
					+ ajuste;
		}
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public String getApellidos() {
		return apellidos;
	}

	public void setApellidos(String apellidos) {
		this.apellidos = apellidos;
	}

	public Date getNacimiento() {
		return nacimiento;
	}

	public void setNacimiento(Date nacimiento) {
		this.nacimiento = nacimiento;
		calcularEdad();
	}

	public int getEdad() {
		return edad;
	}

	public void setEdad(int edad) {
		this.edad = edad;
	}

	public Integer getId() {
		return id;
	}
}

Nota: deberemos cambiar el nivel de compilación en
Eclipse
como mínimo al 1.5, si no lo está ya,
para soportar las anotaciones.

Ahora que tenemos una bonita entidad para persistir, vamos a
crearnos un simple DAO que nos permita guardar un usuario en la base de
datos y recuperar la lista de usuarios.

package modelo;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;

public class UsuarioDao {

	private static SessionFactory sf = new AnnotationConfiguration().configure("hibernate.cfg.xml").buildSessionFactory();
	
	public void guardarUsuario(Usuario usuario) {
		Session sesion = sf.openSession();
		Transaction tx = null;;

		try {
			tx = sesion.beginTransaction();
			sesion.save(usuario);
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		} finally {
			sesion.close();
		}
	}
	
	public List buscarUsuarios() {
		List usuarios = new ArrayList();
		
		Session sesion = sf.openSession();
		Transaction tx = null;;

		try {
			tx = sesion.beginTransaction();
			usuarios = sesion.createQuery("from Usuario").list();
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		} finally {
			sesion.close();
		}

		return usuarios;
	}	
}

Añadimos dentro de la carpeta resources el fichero de configuración de Hibernate.




    
    	org.hibernate.dialect.MySQLInnoDBDialect
        com.mysql.jdbc.Driver
        root
        root
        jdbc:mysql://localhost:3306/eventos?autoReconnect=true

		update
		true

				
	


Hemos añadido la propiedad hibernate.hbm2ddl.auto con el valor auto para que
Hibernate cree de manera automática las tablas en caso de que no existan.
Eso sí, no debemos olvidar crear primero el esquema eventos en la base de datos.

3.3 Creando clases de
prueba y probando la aplicación

Ahora crearemos un par de clases para probar el funcionamiento
de nuestra aplicación. La primera clase guardará una
serie de usuarios en la base de datos. La segunda recuperará
la lista de usuarios y mostrará por la consola su nombre,
apellidos y edad.

Aquí está el código de nuestra
clase PruebaInsercion.

package prueba;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import modelo.Usuario;
import modelo.UsuarioDao;

public class PruebaInsercion {
	public static void main(String[] args) {
		Calendar cal = new GregorianCalendar();
		cal.set(1970, 10, 5);
		nuevoUsuario("Juan", "Lopez", cal.getTime());
		cal.set(1976, 3, 16);
		nuevoUsuario("Enrique", "Perez", cal.getTime());
		cal.set(1973, 5, 9);
		nuevoUsuario("Laura", "Iglesias", cal.getTime());
	}

	private static void nuevoUsuario(String nombre, String apellidos, Date edad) {
		UsuarioDao dao = new UsuarioDao();
		Usuario usuario = new Usuario(nombre, apellidos);
		usuario.setNacimiento(edad);
		dao.guardarUsuario(usuario);
	}
}

Y aquí tenemos la clase PruebaListado, que
mostrará el nombre, apellidos y edad de los usuarios obtenidos
de la base de datos.

package prueba;

import java.util.List;

import modelo.Usuario;
import modelo.UsuarioDao;

public class PruebaListado {
	public static void main(String[] args) {
		UsuarioDao dao = new UsuarioDao();
		List usuarios = dao.buscarUsuarios();
		for (Usuario usuario : usuarios) {
			System.out.printf("%s %s tiene %s años\n", usuario.getNombre(),
					usuario.getApellidos(), usuario.getEdad());
		}
	}
}

Ejecutamos primero la inserción y comprobamos que se
han añadido correctamente los usuarios a nuestra base de datos:

Listado de usuarios en base de datos

Ahora ejecutamos la prueba de listado y observamos...
¡Cáspita! O todos nuestros usuarios son
recién
nacidos o creo que hemos cometido algún error 😉

Listado de usuarios con edad igual a cero

Efectivamente, dijimos que íbamos a hacer que la
función "calcularEdad" se disparase de forma
"automática" al cargar nuestra entidad. ¿Y no era
este un tutorial de eventos en Hibernate?.

Hasta ahora sólamente hemos visto el uso normal de
Hibernate para persistir entidades. A continuación vamos a ver
cómo configurar un oyente para que se ejecute al
dispararse un determinado
evento.

4. Creando un oyente para
escuchar

Una vez preparada nuestra aplicación
sólamente nos resta crear una clase oyente que se ejecute al
cargar cada entidad Usuario. El oyente se encargará de
actualizar el valor de la edad del usuario en función de su
fecha de nacimiento.

Los oyentes en Hibernate son en la práctica
singletons, por lo que
pueden ser compartidos entre peticiones y por tanto no
deberían guardar
ningún tipo de estado.

package oyentes;

import modelo.Usuario;

import org.hibernate.event.PostLoadEvent;
import org.hibernate.event.PostLoadEventListener;

public class PostLoadOyente implements PostLoadEventListener {

	private static final long serialVersionUID = 3672646295810997521L;

	public void onPostLoad(PostLoadEvent event) {
		System.out.println("Ejecutando nuestro oyente");
		if (event.getEntity() instanceof Usuario) {
			Usuario usuario = (Usuario) event.getEntity();
			usuario.calcularEdad();
		}
	}
}

Nuestro implementa la interfaz PostLoadEventListener, para
capturar este tipo de evento. También podríamos haber
extendido la clase PostLoadEvent, el oyente predeterminado de Hibernate.

En Hibernate existem básicamente eventos por
cada método de la clase Session (guardar, borrar, buscar...).
A continuación se muestra una tabla con todos los eventos
existentes:

Evento
Delete PostInsert
DirtyCheck PostLoad
Evict PostUpdate
Flush PreDelete
InitializeCollection PreInsert
Load PreLoad
Lock PreUpdate
Merge Refresh
Persist Replicate
PostDelete SaveOrUpdate

Por cada tipo de evento existe una interfaz, de la forma eventoEventListener
y una clase base de Hibernate de la forma eventoEvent.
Tanto las interfaces como las clases se encuentran dentro del paquete
"org.hibernate.event".

El último paso consiste en asignar el oyente que
hemos programado al evento PostLoad, para que se ejecute justo
después de cargar las entidades. En nuestro caso sólo
existe una entidad, pero un oyente escuchará un evento
determinado independientemente de la entidad sobre la que ocurra el
mismo. Por tanto debemos ser cuidadosos al utilizar nuestro oyente,
asegurándonos de que el evento haya sido producido por la
entidad que creemos.

Para configurar nuestro oyente deberemos añadir las
siguientes líneas al fichero de configuración de
hibernate, dentro de la etiqueta <session-factory>.


  
	


Si volvemos a ejecutar la clase que muestra el listado, esta
vez obtendremos el resultado esperado:

Listado de usuarios con edades correctas

Esta vez el oyente se ha ejecutado justo después de
que se carguen los datos de cada usuario, actualizando el campo edad de
los mismos.

5. Dos mejor que uno

¿Y qué pasa si queremos tener varios oyentes
para un mismo evento? Simplemente tendremos que añadir el
oyente a la pila de oyentes en el fichero de configuración de
hibernate. Todos los oyentes serán llamados en su orden de
aparición; por esta razón es aconsejable mantener
siempre al final de la lista el oyente predeterminado de Hibernate, que
podría realizar acciones adicionales.

El fichero de configuración quedaría ahora
de la siguiente manera:


  
  
  


Y, por supuesto, deberemos añadir nuestra clase
oyente:

package oyentes;

import modelo.Usuario;

import org.hibernate.event.PostLoadEvent;
import org.hibernate.event.PostLoadEventListener;

public class LiftingOyente implements PostLoadEventListener {

	private static final long serialVersionUID = -6982862192025601114L;

	public void onPostLoad(PostLoadEvent event) {
		if (event.getEntity() instanceof Usuario) {
			Usuario usuario = (Usuario) event.getEntity();
			System.out.println("Haciendo lifting a " + usuario.getNombre());
			usuario.setEdad(usuario.getEdad() - 3);
		}
	}
}

Este nuevo oyente aplicará un "lifting" a los
usuarios, rejuveneciéndoles 3 años
(¡qué maravilla!). Si ejecutamos de nuevo el listado
observaremos cómo ha descendido la media de edad de los
usuarios.

Listado con usuarios rejuvenecidos 3 años

6. Conclusiones

A la vista de los resultados de este tutorial podemos extraer
algunas conclusiones:

  • El uso de oyentes nos permite realizar acciones asociadas a
    la persistencia de nuestros objetos de manera automática y
    transparente a nuestra lógica de negocio. Esto nos permite
    realizar acciones que de otra manera tendríamos que controlar
    manualmente cada vez que hiciésemos una llamada a la Session
    de Hibernate.
  • Podemos asignar varios oyentes por cada evento para que
    realizen diferentes tareas y añadir o quitar oyentes
    de manera sencilla por medio del fichero de configuración de
    Hibernate.
  • Deberemos implementar una clase por cada oyente que
    necesitemos. Estas clases deben implementar la interfaz correspondiente
    y distinguir en su código de alguna manera la entidad sobre la
    que se está produciendo el evento.

Ya hemos visto el poder que tienen los eventos para realizar
acciones asociadas con la persistencia y alterar los valores de los
campos de nuestras entidades. No obstante puede resultar un poco
complicado tener que implementar una clase por cada oyente si
simplemente lo que queremos es ejecutar un método de la
entidad que realice ciertas operaciones sobre sus campos. ¿No
sería más sencillo poner simplemente
una anotación en el método que queramos
ejecutar al producirse un evento?  Responderemos a esta y
otras preguntas en la segunda parte de este tutorial.

Y eso es todo. En Autentia siempre estamos buscando soluciones
para los problemas que encontramos en nuestro trabajo cotidiano, y
esperamos que este y otros de nuestros tutoriales os ayuden
también a vosotros a solucionar problemas similares.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad