If you have to react to particular events in your persistence layer, you may also use the Hibernate3 event architecture. The event system can be used in addition or as a replacement for interceptors.
Essentially all of 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 implemenation
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 effectively singletons; meaning, they are shared between requests, and thus should not save any state as instance variables.
A custom listener should implement 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's 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"); } } }
You also need a configuration entry telling Hibernate to use the listener in addition to the default listener:
<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 may 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 the capability 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? Well, 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.