Hibernate.org Community Documentation

- Zurück
- Weiter

## Kapitel 14. Interzeptoren und Ereignisse

It is useful for the application to react to certain events that occur inside Hibernate. This allows for the implementation of generic functionality and the extension of Hibernate functionality.

## 14.1. Interzeptoren

The `Interceptor` interface provides callbacks from the session to the application, allowing the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded. One possible use for this is to track auditing information. For example, the following `Interceptor` automatically sets the `createTimestamp` when an `Auditable` is created and updates the `lastUpdateTimestamp` property when an `Auditable` is updated.

You can either implement `Interceptor` directly or extend `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;

        }

    }

There are two kinds of inteceptors: `Session`-scoped and `SessionFactory`-scoped.

Ein für die `Session` zuständiger Interzeptor wird beim Öffnen einer Session unter Verwendung einer der überlasteten SessionFactory.openSession()-Methoden spezifiziert, die einen `Interceptor` akzeptieren.

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

A `SessionFactory`-scoped interceptor is registered with the `Configuration` object prior to building the `SessionFactory`. Unless a session is opened explicitly specifying the interceptor to use, the supplied interceptor will be applied to all sessions opened from that `SessionFactory`. `SessionFactory`-scoped interceptors must be thread safe. Ensure that you do not store session-specific states, since multiple sessions will use this interceptor potentially concurrently.

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

## 14.2. Ereignissystem

If you have to react to particular events in your persistence layer, you can also use the Hibernate3 event architecture. The event system can be used in addition, or as a replacement, for interceptors.

All the methods of the `Session` interface correlate to an event. You have a `LoadEvent`, a `FlushEvent`, etc. Consult the XML configuration-file DTD or the `org.hibernate.event` package for the full list of defined event types. When a request is made of one of these methods, the Hibernate `Session` generates an appropriate event and passes it to the configured event listeners for that type. Out-of-the-box, these listeners implement the same processing in which those methods always resulted. However, you are free to implement a customization of one of the listener interfaces (i.e., the `LoadEvent` is processed by the registered implementation of the `LoadEventListener` interface), in which case their implementation would be responsible for processing any `load()` requests made of the `Session`.

The listeners should be considered singletons. This means they are shared between requests, and should not save any state as instance variables.

A custom listener implements the appropriate interface for the event it wants to process and/or extend one of the convenience base classes (or even the default event listeners used by Hibernate out-of-the-box as these are declared non-final for this purpose). Custom listeners can either be registered programmatically through the `Configuration` object, or specified in the Hibernate configuration XML. Declarative configuration through the properties file is not supported. Here is an example of a custom load event listener:

    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");

            }

        }

    }

Sie benötigen außerdem einen Konfigurationseintrag, der Hibernate mitteilt, dass der Listener zusätzlich zum Standard-Listener verwendet werden soll:

    <hibernate-configuration>

        <session-factory>

            ...

            <event type="load">

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

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

            </event>

        </session-factory>

    </hibernate-configuration

    >

Instead, you can register it programmatically:

    Configuration cfg = new Configuration();

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

    cfg.EventListeners().setLoadEventListeners(stack);

Listeners registered declaratively cannot share instances. If the same class name is used in multiple `<listener/>` elements, each reference will result in a separate instance of that class. If you need to share listener instances between listener types you must use the programmatic registration approach.

Why implement an interface and define the specific type during configuration? A listener implementation could implement multiple event listener interfaces. Having the type additionally defined during registration makes it easier to turn custom listeners on or off during configuration.

## 14.3. Deklarative Sicherheit in Hibernate

Usually, declarative security in Hibernate applications is managed in a session facade layer. Hibernate3 allows certain actions to be permissioned via JACC, and authorized via JAAS. This is an optional functionality that is built on top of the event architecture.

Zunächst einmal müssen die betreffenden Event-Listener konfiguriert werden, damit die Verwendung der JAAS Authorisierung aktiviert ist.

    <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 that `<listener type="..." class="..."/>` is shorthand for `<event type="..."><listener class="..."/></event>` when there is exactly one listener for a particular event type.

Next, while still in `hibernate.cfg.xml`, bind the permissions to roles:

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

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

Die Rollennamen sind die von Ihrem JACC-Anbieter verstandenen Rollen.

Copyright © 2004 Red Hat, Inc.

- ZurückKapitel 13. Transactions and Concurrency
- Nach oben
- Zum Anfang
- WeiterKapitel 15. Batch-Verarbeitung