Hibernate3的另一个重大变化是采用事件和监听器范式作为其核心处理模型。这允许对外部、由应用程序发起的请求进行非常细粒度的挂钩。甚至允许自定义或完全覆盖Hibernate对这些请求的反应方式。这实际上是对Hibernate早期通过Interceptor、Lifecycle和Validatable接口所尝试实现的功能的扩展。
注意:Lifecycle和Validatable接口已移动到Hibernate3的新&q经典&q包中。不鼓励使用它们,因为它们将Hibernate库的依赖性引入了用户领域模型,这可以通过自定义Interceptor或通过领域类外的新事件模型来处理。这不是什么新东西,因为在Hibernate2的使用中也有同样的建议。
那么,新的Hibernate事件模型定义了哪些类型的事件?本质上,org.hibernate.Session接口的所有方法都与一个事件相关。因此,你有一个LoadEvent、一个FlushEvent等(查阅配置DTD或org.hibernate.event包以获取定义的事件类型的完整列表)。当一个方法被请求时,Hibernate会生成相应的事件并将其传递给配置的该类型的事件监听器。默认情况下,这些监听器实现了那些方法始终产生的相同处理。然而,用户可以自由实现一个自定义的监听器接口(即,LoadEvent由注册的LoadEventListener接口的实现处理),在这种情况下,他们的实现将负责处理对Session的任何load()请求。
这些监听器应被视为实际上是单例的;也就是说,它们在请求之间共享,因此不应将任何状态保存为实例变量。然而,事件对象本身确实包含处理所需的大量上下文,因为它们对每个请求都是唯一的。自定义事件监听器也可以利用事件的上下文来存储所需的任何处理变量。上下文是一个简单的映射,但默认监听器根本不使用上下文映射,所以请放心不会覆盖内部所需的上下文变量。
自定义监听器应实现它想要处理的事件的相应接口,并/或扩展其中一个便利的基类(甚至可以使用Hibernate默认事件监听器,因为这些基类已声明为非final,以便用于此目的)。自定义监听器可以通过配置对象程序化注册,也可以在Hibernate配置XML中指定(不支持通过属性文件进行声明性配置)。以下是一个自定义加载事件监听器的示例
public class MyLoadListener extends DefaultLoadEventListener { // this is the single method defined by the LoadEventListener interface public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType) throws HibernateException { if ( !MySecurity.isAuthorized( event.getEntityName(), event.getEntityId() ) ) { throw MySecurityException("Unauthorized access"); } return super.onLoad(event, loadType); } }
然后我们需要一个配置条目,告诉Hibernate使用我们的监听器而不是默认监听器
<hibernate-configuration> <session-factory> ... <listener type="load" class="MyLoadListener"/> </session-factory> </hibernate-configuration>
或者我们可以程序化地注册它
Configuration cfg = new Configuration(); cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() ); ....
声明性注册的监听器不能共享实例。如果多个
为什么在配置时实现接口并定义特定类型?嗯,一个监听器实现可以实现多个事件监听器接口。在注册时定义类型可以更容易地在配置期间打开或关闭自定义监听器。