Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 14. Interceptores y eventos

Es útil para la aplicación reaccionar a ciertos eventos que ocurren dentro de Hibernate. Esto permite la implementación de funcionalidades genéricas y la extensión de la funcionalidad de Hibernate.

## 14.1. Interceptores

La interfaz `Interceptor` brinda callbacks desde la sesión a la aplicación, permitiendole a ésta última inspeccionar y/o manipular las propiedades de un objeto persistente antes de que sea guardado, actualizado, borrado o cargado. Un uso posible de esto es seguir la pista de la información de auditoría. Por ejemplo, el siguiente `Interceptor` establece automáticamente el `createTimestamp` cuando se crea un `Auditable` y se actualiza la propiedad `lastUpdateTimestamp` cuando se actualiza un `Auditable`.

Puede implementar el `Interceptor` directamente o extender el `EmptyInterceptor`.

    package org.hibernate.test;

    import java.io.Serializable;

    import java.util.Date;

    import java.util.Iterator;

    import org.hibernate.EmptyInterceptor;

    import org.hibernate.Transaction;

    import org.hibernate.type.Type;

    public class AuditInterceptor extends EmptyInterceptor {

        private int updates;

        private int creates;

        private int loads;

        public void onDelete(Object entity,

                             Serializable id,

                             Object[] state,

                             String[] propertyNames,

                             Type[] types) {

            // do nothing

        }

        public boolean onFlushDirty(Object entity,

                                    Serializable id,

                                    Object[] currentState,

                                    Object[] previousState,

                                    String[] propertyNames,

                                    Type[] types) {

            if ( entity instanceof Auditable ) {

                updates++;

                for ( int i=0; i < propertyNames.length; i++ ) {

                    if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {

                        currentState[i] = new Date();

                        return true;

                    }

                }

            }

            return false;

        }

        public boolean onLoad(Object entity,

                              Serializable id,

                              Object[] state,

                              String[] propertyNames,

                              Type[] types) {

            if ( entity instanceof Auditable ) {

                loads++;

            }

            return false;

        }

        public boolean onSave(Object entity,

                              Serializable id,

                              Object[] state,

                              String[] propertyNames,

                              Type[] types) {

            if ( entity instanceof Auditable ) {

                creates++;

                for ( int i=0; i<propertyNames.length; i++ ) {

                    if ( "createTimestamp".equals( propertyNames[i] ) ) {

                        state[i] = new Date();

                        return true;

                    }

                }

            }

            return false;

        }

        public void afterTransactionCompletion(Transaction tx) {

            if ( tx.wasCommitted() ) {

                System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);

            }

            updates=0;

            creates=0;

            loads=0;

        }

    }

Hay dos clases de interceptores: incluído en `Session`- e incluído en `SessionFactory`.

Se especifica un interceptor incluído `Session` cuando se abre una sesión utilizando uno de los métodos SessionFactory.openSession() sobrecargados aceptando un `Interceptor`.

    Session session = sf.openSession( new AuditInterceptor() );

Un interceptor incluido en `SessionFactory` se encuentra registrado con el objeto `Configuration` antes de construir el `SessionFactory`. En este caso, el interceptor proveido será aplicado a todas las sesiones abiertas desde ese `SessionFactory`; a menos de que se abra una sesión especificando explícitamente el interceptor a utilizar. Los interceptores `SessionFactory` incluidos deben ser a prueba de hilos. Asegúrese de no almacenar un estado especifico a la sesión ya que múltiples sesiones utilizarán este interceptor potencialmente de manera concurrente.

    new Configuration().setInterceptor( new AuditInterceptor() );

## 14.2. Sistema de eventos

Si tiene que reaccionar a eventos particulares en su capa de persistencia, también puede utilizar la arquitectura de eventos de Hibernate3. El sistema de eventos se puede ser utilizar además de o como un remplazo para los interceptores.

Todos los métodos de la interfaz `Session` se correlacionan con un evento. Tiene un `LoadEvent`, un `FlushEvent`, etc. Consulte el DTD del archivo de configuración XML o el paquete `org.hibernate.event` para ver la lista completa de los tipos de eventos definidos. Cuando se realiza una petición de uno de estos métodos, la `Session` de Hibernate genera un evento apropiado y se lo pasa al escucha (listener) de eventos configurado para ese tipo. Tal como vienen, estos escuchas implementan el mismo procesamiento en aquellos métodos donde siempre resultan . Sin embargo, usted es libre de implementar una personalización de una de las interfaces escuchas (por ejemplo, el `LoadEvent` es procesado por la implementación registrada de la interfaz `LoadEventListener`), en cuyo caso su implementación sería responsable de procesar cualquier petición `load()` realizada a la `Session`.

Los escuchas se deben considerar como singletons. Esto significa que son compartidos entre las peticiones y por lo tanto, no deben guardar ningún estado como variables de instancia.

Un escucha personalizado implementa la interfaz apropiada para el evento que quiere procesar y/o extender una de las clases base de conveniencia (o incluso los escuchas de eventos predeterminados utilizados por Hibernate de fábrica al declararlos como no-finales para este propósito). Los escuchas personalizados pueden ser registrados programáticamente a través del objeto `Configuration`, o especificados en el XML de configuración de Hibernate. No se soporta la configuración declarativa a través del archivo de propiedades. Este es un ejemplo de un escucha personalizado de eventos load:

    public class MyLoadListener implements LoadEventListener {

        // this is the single method defined by the LoadEventListener interface

        public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)

                throws HibernateException {

            if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {

                throw MySecurityException("Unauthorized access");

            }

        }

    }

También necesita una entrada de configuración diciéndole a Hibernate que utilice el oyente en vez del oyente por defecto:

    <hibernate-configuration>

        <session-factory>

            ...

            <event type="load">

                <listener class="com.eg.MyLoadListener"/>

                <listener class="org.hibernate.event.def.DefaultLoadEventListener"/>

            </event>

        </session-factory>

    </hibernate-configuration

    >

En cambio, puede registrarlo programáticamente:

    Configuration cfg = new Configuration();

    LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };

    cfg.EventListeners().setLoadEventListeners(stack);

Los oyentes registrados declarativamente no pueden compartir instancias. Si se utiliza el mismo nombre de clase en múltiples elementos `<listener/>`, cada referencia resultará en una instancia separada de esa clase. Si necesita compartir instancias de oyentes entre tipos de oyentes debe usar el enfoque de registración programática.

¿Por qué implementar una interfaz y definir el tipo específico durante la configuración? Una implementación de escucha podría implementar múltiples interfaces de escucha de eventos. Teniendo el tipo definido adicionalmente durante la registración hace más fácil activar o desactivar escuchas personalizados durante la configuración.

## 14.3. Seguridad declarativa de Hibernate

Usualmente, la seguridad declarativa en aplicaciones Hibernate se administra en una capa de fachada de sesión. Hibernate3 permite que ciertas acciones se permitan por medio de JACC y las autoriza por medio de JAAS. Esta es una funcionalidad opcional construída encima de la arquitectura de eventos.

Primero, tiene que configurar los oyentes de eventos apropiados, para habilitar la utilización de autorización JAAS.

    <listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>

    <listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>

    <listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>

    <listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>

Note que `<listener type="..." class="..."/>` es la abreviatura para `<event type="..."><listener class="..."/></event>` cuando hay exactamente un escucha para un tipo de evento en particular.

A continuación, todavía en `hibernate.cfg.xml`, enlace los permisos a los roles:

    <grant role="admin" entity-name="User" actions="insert,update,read"/>

    <grant role="su" entity-name="User" actions="*"/>

Los nombres de los roles son comprendidos por su proveedor de JACC.

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 13. Transacciones y concurrencia
- Subir
- Inicio
- SiguienteCapítulo 15. Procesamiento por lotes