unidades:06_objetos_validaciones:04_listeners
Diferencias
Muestra las diferencias entre dos versiones de la página.
— | unidades:06_objetos_validaciones:04_listeners [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1 | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
+ | ====== Listeners y reglas de negocio ====== | ||
+ | Los listeners en Hibernate son como los Tiggers en las bases de datos relacionales. En la versión 4 de Hibernate se ha modificado la forma en la que se registran los listeners, lo que implica que la documentación es escasa en caso de querer ampliar la documentación de éste curso. | ||
+ | Un listener es simplemente una función Java que se ejecutará ante un evento que ocurra en Hibernate. Hibernate dispone de gran cantidad de Listeners. Los más importantes se explican en la siguiente tabla: | ||
+ | |||
+ | ^ Evento ^ Interfaz ^ Descripción ^ | ||
+ | | PreInsert | <javadoc h41> | ||
+ | | PreLoad | <javadoc h41> | ||
+ | | PreUpdate | <javadoc h41> | ||
+ | | PreDelete | <javadoc h41> | ||
+ | | PostInsert | <javadoc h41> | ||
+ | | PostLoad | <javadoc h41> | ||
+ | | PostUpdate | <javadoc h41> | ||
+ | | PostDelete | <javadoc h41> | ||
+ | |||
+ | ===== Configuración ===== | ||
+ | Antes de la versión 4 de Hibernate los listeners se definían en el fichero '' | ||
+ | |||
+ | Lo primero es que ya no es posible definirlos en un fichero de configuración sino que hay que registrarlos a través de una clase que implemente el interfaz <javadoc h41> | ||
+ | |||
+ | En el proyecto que vayamos a usar los listeners deberemos hacer los siguientes cambios: | ||
+ | - Crear el paquete '' | ||
+ | - En el paquete '' | ||
+ | - En el paquete '' | ||
+ | - Crear la carpeta '' | ||
+ | - Dentro de la carpeta '' | ||
+ | - En la carpeta '' | ||
+ | - Añadir al fichero | ||
+ | |||
+ | El código fuente de las 2 clases Java que hay que crear es el siguiente: | ||
+ | <code java | GenericIntegratorImpl.java> | ||
+ | package com.fpmislata.persistencia.hibernate.util; | ||
+ | |||
+ | import org.hibernate.cfg.Configuration; | ||
+ | import org.hibernate.engine.spi.SessionFactoryImplementor; | ||
+ | import org.hibernate.event.service.spi.EventListenerRegistry; | ||
+ | import org.hibernate.event.spi.EventType; | ||
+ | import org.hibernate.integrator.spi.Integrator; | ||
+ | import org.hibernate.metamodel.source.MetadataImplementor; | ||
+ | import org.hibernate.service.spi.SessionFactoryServiceRegistry; | ||
+ | |||
+ | |||
+ | public class GenericIntegratorImpl implements Integrator { | ||
+ | |||
+ | @Override | ||
+ | public void integrate(Configuration c, SessionFactoryImplementor sfi, SessionFactoryServiceRegistry sfsr) { | ||
+ | final EventListenerRegistry eventListenerRegistry = sfsr.getService(EventListenerRegistry.class); | ||
+ | |||
+ | prependListeners(eventListenerRegistry); | ||
+ | |||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void integrate(MetadataImplementor mi, SessionFactoryImplementor sfi, SessionFactoryServiceRegistry sfsr) { | ||
+ | final EventListenerRegistry eventListenerRegistry = sfsr.getService(EventListenerRegistry.class); | ||
+ | |||
+ | prependListeners(eventListenerRegistry); | ||
+ | |||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void disintegrate(SessionFactoryImplementor sfi, SessionFactoryServiceRegistry sfsr) { | ||
+ | } | ||
+ | |||
+ | private void prependListeners(EventListenerRegistry eventListenerRegistry) { | ||
+ | eventListenerRegistry.prependListeners(EventType.PRE_INSERT, | ||
+ | eventListenerRegistry.prependListeners(EventType.PRE_LOAD, | ||
+ | eventListenerRegistry.prependListeners(EventType.PRE_UPDATE, | ||
+ | eventListenerRegistry.prependListeners(EventType.PRE_DELETE, | ||
+ | eventListenerRegistry.prependListeners(EventType.POST_INSERT, | ||
+ | eventListenerRegistry.prependListeners(EventType.POST_LOAD, | ||
+ | eventListenerRegistry.prependListeners(EventType.POST_UPDATE, | ||
+ | eventListenerRegistry.prependListeners(EventType.POST_DELETE, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Esta clase que implementa el interfaz <javadoc h41> | ||
+ | |||
+ | <code java | GenericEventListenerImpl.java> | ||
+ | package com.fpmislata.persistencia.hibernate.util; | ||
+ | |||
+ | import org.hibernate.event.spi.*; | ||
+ | |||
+ | public class GenericEventListenerImpl implements PreInsertEventListener, | ||
+ | |||
+ | @Override | ||
+ | public boolean onPreInsert(PreInsertEvent pie) { | ||
+ | Object entity=pie.getEntity(); | ||
+ | if (entity instanceof PreInsertEventListener) { | ||
+ | return ((PreInsertEventListener)entity).onPreInsert(pie); | ||
+ | } else { | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onPreLoad(PreLoadEvent ple) { | ||
+ | Object entity=ple.getEntity(); | ||
+ | if (entity instanceof PreLoadEventListener) { | ||
+ | ((PreLoadEventListener)entity).onPreLoad(ple); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean onPreUpdate(PreUpdateEvent pue) { | ||
+ | Object entity=pue.getEntity(); | ||
+ | if (entity instanceof PreUpdateEventListener) { | ||
+ | return ((PreUpdateEventListener)entity).onPreUpdate(pue); | ||
+ | } else { | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean onPreDelete(PreDeleteEvent pde) { | ||
+ | Object entity=pde.getEntity(); | ||
+ | if (entity instanceof PreDeleteEventListener) { | ||
+ | return ((PreDeleteEventListener)entity).onPreDelete(pde); | ||
+ | } else { | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onPostInsert(PostInsertEvent pie) { | ||
+ | Object entity=pie.getEntity(); | ||
+ | if (entity instanceof PostInsertEventListener) { | ||
+ | ((PostInsertEventListener)entity).onPostInsert(pie); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onPostLoad(PostLoadEvent ple) { | ||
+ | Object entity=ple.getEntity(); | ||
+ | if (entity instanceof PostLoadEventListener) { | ||
+ | ((PostLoadEventListener)entity).onPostLoad(ple); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onPostUpdate(PostUpdateEvent pue) { | ||
+ | Object entity=pue.getEntity(); | ||
+ | if (entity instanceof PostUpdateEventListener) { | ||
+ | ((PostUpdateEventListener)entity).onPostUpdate(pue); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void onPostDelete(PostDeleteEvent pde) { | ||
+ | Object entity=pde.getEntity(); | ||
+ | if (entity instanceof PostDeleteEventListener) { | ||
+ | ((PostDeleteEventListener)entity).onPostDelete(pde); | ||
+ | } | ||
+ | } | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Esta clase es un listener de cada uno de los 8 tipos que hemos explicado. Realiza la tarea de llamar al listener correspondiente de la entidad que ha provocado la llamada al listener. | ||
+ | ===== Definiendo Listeners ===== | ||
+ | Ahora ya podemos usar los listeners de Hibernate de una forma sencilla. Si queremos que una entidad ejecute un método en un evento concreto, no hay más que implementar el interfaz correspondiente y dicho método se ejecutará automáticamente. En la tabla del principio se han listado los posibles eventos y el interfaz que es necesario implementar. | ||
+ | |||
+ | Veamos un ejemplo para aclarar el funcionamiento: | ||
+ | |||
+ | Queremos que en la entidad '' | ||
+ | |||
+ | Veamos el código Java para explicar el proceso: | ||
+ | <code java 1> | ||
+ | public class Usuario implements Serializable, | ||
+ | | ||
+ | private int idUsuario; | ||
+ | private String login; | ||
+ | private String nombre; | ||
+ | private String ape1; | ||
+ | private String ape2; | ||
+ | private String password; | ||
+ | private String confirmPassword; | ||
+ | private Date fechaCreacion; | ||
+ | |||
+ | public Usuario() { | ||
+ | } | ||
+ | |||
+ | public Usuario(String login, String nombre, String ape1, String ape2, String password, String confirmPassword) { | ||
+ | this.login = login; | ||
+ | this.nombre = nombre; | ||
+ | this.ape1 = ape1; | ||
+ | this.ape2 = ape2; | ||
+ | this.password = password; | ||
+ | this.confirmPassword = confirmPassword; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean onPreInsert(PreInsertEvent pie) { | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * En la línea 1 podemos apreciar cómo la clase '' | ||
+ | * En las líneas 24 a 27 se ve el método <javadoc h41> | ||
+ | |||
+ | Vemos que el proceso es muy sencillo, simplemente hay que implementar el interfaz correspondiente al evento que queremos tratar.Ya no es necesario ningún tipo de configuración al respecto. | ||
+ | |||
+ | <note important> | ||
+ | El problema de usar listeners en nuestras entidades es que ahora nuestro código ya está //atado// a Hibernate, ya ahora implementa un interfaz que sólo existe en Hibernate. | ||
+ | Una solución bastante sencilla sería crearte tus propios interfaces o anotaciones similares a los tipos de eventos y modificar la clase '' | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | Se podría argumentar que el código de los listeners no debería estar en la misma clase que las entidades , en ese caso, sería muy sencillo cambiar el código de '' | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | En JPA existen anotaciones como <javadoc jee6> | ||
+ | </ | ||
+ | ===== Usando Listeners ===== | ||
+ | Ya tenemos definido el método que se ejecutará según el evento que hemos establecido. Ahora veamos cómo se utiliza. En el caso del evento // | ||
+ | |||
+ | Expliquemos ahora los métodos más importantes de la clase <javadoc h41> | ||
+ | |||
+ | * '' | ||
+ | Nos retorna el propio objeto que estamos persistiendo.Sobre este objeto deberemos modificar los datos. | ||
+ | |||
+ | * '' | ||
+ | Nos retorna un array con los datos de la entidad. Este array es importantísimo ya que sobre él deberemos también hacer cambios en los datos y se reflejarán al persistirse. El problema es que es un array de datos, con lo que necesitamos el índice de cada propiedad. | ||
+ | * '' | ||
+ | Esta array nos va a ayudar a saber el índice de una propiedad en función de su nombre. Para ello necesitaremos la siguiente función: | ||
+ | <code java> | ||
+ | /** | ||
+ | * Obtiene el índice de una propiedad en función de su nombre | ||
+ | * @param propertyNames Array con el nombre de las propiedades de una entidad | ||
+ | * @param propertyName Nombre de la entidad de la que queremos obtener su índice | ||
+ | * @return El indice de la propiedad o -1 si no existe la propiedad. | ||
+ | */ | ||
+ | private int getPropertyNameIndex(String[] propertyNames, | ||
+ | for(int i=0; | ||
+ | if (propertyNames[i].equals(propertyName)) { | ||
+ | return i; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | return -1; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Ahora que tenemos la función '' | ||
+ | |||
+ | <code java 1 | Uso de onPreInsert> | ||
+ | public class Usuario implements Serializable, | ||
+ | |||
+ | private int idUsuario; | ||
+ | private String login; | ||
+ | private String nombre; | ||
+ | private String ape1; | ||
+ | private String ape2; | ||
+ | private String password; | ||
+ | private String confirmPassword; | ||
+ | private Date fechaCreacion; | ||
+ | |||
+ | public Usuario() { | ||
+ | } | ||
+ | |||
+ | public Usuario(String login, String nombre, String ape1, String ape2, String password, String confirmPassword) { | ||
+ | this.login = login; | ||
+ | this.nombre = nombre; | ||
+ | this.ape1 = ape1; | ||
+ | this.ape2 = ape2; | ||
+ | this.password = password; | ||
+ | this.confirmPassword = confirmPassword; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public boolean onPreInsert(PreInsertEvent pie) { | ||
+ | int propertyNameIndex = getPropertyNameIndex(pie.getPersister().getPropertyNames(), | ||
+ | Date fechaCreacion=new Date(); | ||
+ | pie.getState()[propertyNameIndex] = fechaCreacion; | ||
+ | ((Usuario)(pie.getEntity())).setFechaCreacion(fechaCreacion); | ||
+ | |||
+ | return false; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Obtiene el índice de una propiedad en función de su nombre | ||
+ | * @param propertyNames Array con el nombre de las propiedades de una entidad | ||
+ | * @param propertyName Nombre de la entidad de la que queremos obtener su índice | ||
+ | * @return El indice de la propiedad o -1 si no existe la propiedad. | ||
+ | */ | ||
+ | private int getPropertyNameIndex(String[] propertyNames, | ||
+ | for (int i = 0; i < propertyNames.length; | ||
+ | if (propertyNames[i].equals(propertyName)) { | ||
+ | return i; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | return -1; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * De las líneas 34 a 48 está la nueva función '' | ||
+ | * En la línea 26 se obtiene el índice de la propiedad '' | ||
+ | * En la línea 27 creamos la fecha que queremos establecer en el objeto. | ||
+ | * En la línea 28 modificamos el array que contiene los datos de la entidad estableciendo la fecha. | ||
+ | * En la línea 29 modificamos el objeto estableciendo la fecha. | ||
+ | * En la línea 31 se retorna '' | ||
+ | |||
+ | <note tip> | ||
+ | Recuerda que se necesitan cambiar los datos tanto en el objeto ( '' | ||
+ | </ | ||
+ | |||
+ | <note warning> | ||
+ | Recuerda siempre retornar '' | ||
+ | </ | ||
+ | ===== Reglas de negocio ===== | ||
+ | Las reglas de negocio es lo más útil que podemos hacer con los listeners de Hibernate.Veamos los tipos de reglas que podemos implementar: | ||
+ | * **Restricciones**: | ||
+ | * **Acciones**: | ||
+ | * Enviar un correo electrónico de bienvenida cuando se da de alta usuario | ||
+ | * Hacer un cargo en la tarjeta de crédito al comprar un producto. | ||
+ | * Enviar un SMS para confirmar un pago. | ||
+ | * Etc. | ||
+ | * **Cálculos derivados**: | ||
+ | * Calcular el importe total de una compra sumando los importes de los productos. | ||
+ | * Añadir la fecha de creación de un usuario automáticamente. | ||
+ | * Calcular el importe de una línea de factura en función del número de elementos comprados y su precio. | ||
+ | * Etc. | ||
+ | |||
+ | Las reglas de negocio son una parte muy importante de una aplicación.En aplicaciones empresariales es realmente fundamental ya que pueden emplear la mayor parte de los recursos al desarrollar la aplicación. También puede ser la parte más cambiante ya que las reglas en una empresa son una parte muy dinámica de la misma. | ||
+ | |||
+ | Sin embargo en mi opinión a las reglas de negocio no se les da la debida importancia.A continuación encontraréis una serie de páginas donde tratan el tema de las reglas de negocio. | ||
+ | * [[http:// | ||
+ | * [[https:// | ||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | |||
+ | ===== Referencias ===== | ||
+ | |||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | * [[http:// |