4.8 事 件 机 制

通常,Hibernate执行持久化过程中,应用程序无法参与其中。所有的数据持久化操作,对用户都是透明的,用户无法插入自己的动作。

通过事件框架,Hibernate允许应用程序能响应特定的内部事件,从而允许实现某些通用的功能,或对Hibernate功能进行扩展。

Hibernate的事件框架由两个部分组成:

● 拦截器机制,对于特定动作拦截,回调应用中的特定动作。

● 事件系统,重写Hibernate的事件监听器。

4.8.1 拦截器

通过Interceptor接口,可以从Session中回调应用程序的特定方法,这种回调机制可让应用程序在持久化对象被保存、更新、删除或加载之前,检查并修改其属性。

通过Interceptor接口,可以在数据进入数据库之间,对数据进行最后的检查,如果数据不符合要求,可以修改数据,从而避免非法数据进入数据库。当然,通常无须这样做,只是在某些特殊的场合下,才考虑使用拦截器完成检查功能。

使用拦截器可按如下步骤进行:

(1)定义实现Interceptor接口的拦截器类;

(2)通过Session启用拦截器,或者通过Configuration启用全局拦截器。

下面是一个拦截器的示例代码,该拦截器没有进行任何实际的操作,仅仅打印出标志代码:

public class MyInterceptor extends EmptyInterceptor

{

//更新的次数

private int updates;

//插入的次数

private int creates;

//删除数据时,将调用onDelete方法

public void onDelete(Object entity,Serializable id,Object[]

state,String[] propertyNames, Type[] types)

{

//do nothing

}

//同步Session和数据库中的数据

public boolean onFlushDirty(Object entity, Serializable id, Object[]

currentState, Object[] previousState, String[] propertyNames, Type[]

types)

{

//每同步一次,修改的累加器加1

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)

{

System.out.println("========================");

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

{

if ( "name".equals( propertyNames[i] ) )

{

System.out.println(state[i]);

state[i] = "aaa";

return true;

}

}

return false;

}

//保存持久化实例时,调用该方法

public boolean onSave(Object entity,Serializable id,Object[]

state,String[] propertyNames,Type[] types)

{

creates++;

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

{

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

{

state[i] = new Date();

return true;

}

}

return false;

}

//提交刷新

public void postFlush(Iterator entities)

{

System.out.println("创建的次数: " + creates + ", 更新的次数: " +

updates);

}

public void preFlush(Iterator entities)

{

updates=0;

creates=0;

}

//事务提交前,触发该方法

public void beforeTransactionCompletion(Transaction tx)

{

System.out.println("事务即将结束");

}

//事务提交后,触发该方法

public void afterTransactionCompletion(Transaction tx)

{

System.out.println("事务已经结束");

}

}

在上面的拦截器实现类中,实现了很多方法,这些方法都是在Hibernate执行特定动作时自动调用。

完成了拦截器的定义,下面是关于拦截器的使用。拦截器的使用有两种方法:

● 通过SessionFactory的openSession(Interceptor in)方法打开一个带局部拦截器的Session。

● 通过Configuration的setInterceptor(Interceptor in)方法设置全局拦截器。

下面是使用局部拦截器的示例代码:

public class HibernateUtil

{

//静态类属性 SessionFactory

public static final SessionFactory sessionFactory;

//静态初始化块,完成静态属性的初始化

static

{

try

{

//采用默认的hibernate.cfg.xml来启动一个Configuration的实例

Configuration configuration=new Configuration().configure();

//由Configuration的实例来创建一个SessionFactory实例

sessionFactory = configuration.buildSessionFactory();

}

catch (Throwable ex)

{

System.err.println("初始化sessionFactory失败." + ex);

throw new ExceptionInInitializerError(ex);

}

}

//ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要

对线程同步

public static final ThreadLocal session = new ThreadLocal();

//不加拦截器的打开Session方法

public static Session currentSession() throws HibernateException

{

Session s = (Session) session.get();

//如果该线程还没有Session,则创建一个新的Session

if (s == null)

{

s = sessionFactory.openSession();

//将获得的Session变量存储在ThreadLocal变量的Session里

session.set(s);

}

return s;

}

//加拦截器的打开Session方法

public static Session currentSession(Interceptor it) throws

HibernateException

{

Session s = (Session) session.get();

//如果该线程还没有Session,则创建一个新的Session

if (s == null)

{

//以拦截器创建Session对象

s = sessionFactory.openSession(it);

//将获得的Session变量存储在ThreadLocal变量的Session里

session.set(s);

}

return s;

}

//关闭Session对象

public static void closeSession() throws HibernateException

{

Session s = (Session) session.get();

if (s != null)

s.close();

session.set(null);

}

}

上面的Hibernate工具类提供了两个currentSession方法,分别用于不使用拦截器获取Session对象和使用拦截器获取Session对象。

下面是主程序使用拦截器的代码片段:

private void testUser()

{

//以拦截器开始Session

Session session = HibernateUtil.currentSession(new MyInterceptor());

//开始事务

Transaction tx = session.beginTransaction();

//执行下面的代码时,可以看到系统回调onSave等方法

/*

User u = new User();

u.setName("Yeeku Lee");

u.setAge(28);

u.setNationality("中国");

session.persist(u);

u.setAge(29);

u.setAge(30);

session.persist(u);

*/

//执行下面的代码时,可以看到系统回调onLoad等方法

Object o = session.load(User.class , new Integer(1));

System.out.println(o);

User u = (User)o;

System.out.println(u.getName());

//提交事务时,可以看到系统回调事务相关方法

tx.commit();

HibernateUtil.closeSession();

}

4.8.2 事件系统

Hibernate 3的事件系统是功能更强大的事件框架,事件系统可以替代拦截器,也可以作为拦截器的补充来使用。

基本上,Session接口的每个方法都有对应的事件。如LoadEvent和FlushEvent等。当Session调用某个方法时,Hibernate Session会生成对应的事件,并激活对应的事件监听器。

系统默认监听器实现的处理过程,完成了所有的数据持久化操作,包括插入和修改等操作。如果用户定义了自己的监听器,则意味着用户必须完成对象的持久化操作。

例如,可以在系统中实现并注册LoadEventListener监听器,该监听器负责处理所有调用Session的load()方法的请求。

监听器是单态模式对象,即所有同类型的事件处理共享同一个监听器实例,因此监听器不应该保存任何状态,即不应该使用成员变量。

使用事件系统可按如下步骤进行:

(1)实现自己的事件监听器类;

(2)注册自定义事件监听器,代替系统默认的事件监听器。

实现用户的自定义监听器有如下3个方法:

● 实现对应的监听器接口,这是不可思议的,实现接口必须实现接口内的所有方法,关键是必须实现Hibernate对应的持久化操作,即数据库访问,这意味着程序员完全取代了Hibernate的底层操作。

● 继承事件适配器,可以选择性地实现需要关注的方法,但依然试图取代Hibernate完成数据库的访问,这也不太现实。

● 继承系统默认的事件监听器,扩展特定方法。

实际上,前两种方法很少使用。因为Hibernate的持久化操作也是通过这些监听器实现的,如果用户取代了这些监听器,则应该自己实现所有的持久化操作,这意味着用户放弃了Hibernate的持久化操作,而改为自己完成Hibernate的核心操作。

通常推荐采用第三种方法实现自己的事件监听器。Hibernate默认的事件监听器都被声明成non-final,从而方便用户继承。

下面是用户自定义监听器的示例:

//自定义LoadListener,继承默认的DefaultLoadEventListener实现类

public class MyLoadListener extends DefaultLoadEventListener

{

//在LoadEventListener接口仅仅定义了这个方法

public Object onLoad(LoadEvent event, LoadEventListener.LoadType

loadType)throws HibernateException

{

//先调用父类的onLoad方法,从而完成默认的持久化操作

Object o = super.onLoad(event, loadType);

//加入用户的自定义处理

System.out.println("自定义的load事件");

System.out.println(event.getEntityClassName() + "==========" +

event.getEntityId());

return o;

}

}

下面还有一个MySaveListener,用于监听SaveEvent事件:

//自定义SavaListener,继承默认的DefaultSaveEventListener实现类

public class MySaveListener extends DefaultSaveEventListener

{

//该方法完成实际的数据插入动作

protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event)

{

//先执行用户自定义的操作

System.out.println(event.getObject());

//调用父类的默认持久化操作

return super.performSaveOrUpdate(event);

}

}

注意:扩展用户自定义监听器时,别忘了在方法中调用父类的对应方法。

注册用户自定义监听器也有两种方法:

● 编程式,通过使用Configuration对象编程注册。

● 声明式,在Hibernate的XML格式配置文件中进行声明,使用Properties格式的配置文件将无法配置自定义监听器。

下面的示例代码,通过编程方式使用自定义监听器:

public class HibernateUtil2

{

//静态类属性 SessionFactory

public static final SessionFactory sessionFactory;

//静态初始化块,完成静态属性的初始化

static

{

try

{

Configuration cfg = new Configuration();

//注册loadEventListener监听器

cfg.getSessionEventListenerConfig().setLoadEventListener

( new MyLoadListener() );

//注册saveListener监听器

cfg.getSessionEventListenerConfig().setSaveEventListener

(new MySaveListener() );

//由Configuration实例来创建一个SessionFactory实例

sessionFactory = cfg.configure().buildSessionFactory();

}

catch (Throwable ex)

{

System.err.println("初始化sessionFactory失败." + ex);

throw new ExceptionInInitializerError(ex);

}

}

//ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要

对线程同步

public static final ThreadLocal session = new ThreadLocal();

//不加拦截器的打开Session方法

public static Session currentSession() throws HibernateException

{

Session s = (Session) session.get();

//如果该线程还没有Session,则创建一个新的Session

if (s == null)

{

s = sessionFactory.openSession();

//将获得的Session变量存储在ThreadLocal变量的Session里

session.set(s);

}

return s;

}

//关闭Session对象

public static void closeSession() throws HibernateException

{

Session s = (Session) session.get();

if (s != null)

s.close();

session.set(null);

}

}

如果不想修改代码,也可以在配置文件中使用事件监听器,注册事件监听器的Hibernate配置文件代码如下:

<?xml version='1.0' encoding='GBK'?>

<!-- Hibernate配置文件的文件头,包含DTD等信息 -->

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.

dtd">

<!-- Hibernate配置文件的根元素 -->

<hibernate-configuration>

<session-factory>

<!—设置数据库驱动 -->

<property name="connection.driver_class">com.mysql.jdbc.Driver

</property>

<!-- 数据库服务的url -->

<property name="connection.url">jdbc:mysql://localhost/hibernate

</property>

<!-- 数据库服务的用户名 -->

<property name="connection.username">root</property>

<!-- 数据库服务的密码 -->

<property name="connection.password">32147</property>

<!-- JDBC connection pool (use the built-in) -->

<property name="connection.pool_size">5</property>

<!-- 设置数据库方言 -->

<property name="dialect">org.hibernate.dialect.MySQLDialect

</property>

<!-- 显示Hibernate生成的SQL语句 -->

<property name="show_sql">true</property>

<!-- 配置应用启动时,是否启动自动建表 -->

<property name="hbm2ddl.auto">update</property>

<!-- 列出所有的持久化映射文件 -->

<mapping resource="User.hbm.xml"/>

<!-- 注册事件监听器 -->

<listener type="load" class="lee.MyLoadListener"/>

<listener type="save" class="lee.MySaveListener"/>

</session-factory>

</hibernate-configuration>

使用配置文件注册事件监听器虽然方便,但也有不利之处,通过配置文件注册的监听器不能共享实例。如果多个<listener/>元素中使用了相同的类,则每一个引用都将产生一个新的拦截器实例。如果需要在多个事件之间共享监听器的实例,则必须使用编程方式注册事件监听器。

注意:虽然监听器类实现了特定监听器的接口,在注册的时候还要明确指出注册的事件。这是因为一个类可能实现多个监听器的接口,注册时明确指定要监听的事件,可以使得启用或者禁用某个事件监听的配置工作更简单。

转载于:https://www.cnblogs.com/jadmin/archive/2009/07/19/2206094.html

Hibernate的事件机制相关推荐

  1. 「前端面试题系列7」Javascript 中的事件机制(从原生到框架)

    前言 这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到: 理解函数的柯里化 ES6 中箭头函数的用法 this 的原理以及用法 伪类与伪元素的区别及实战 如何实现一个圣杯布局? ...

  2. jQuery中的事件机制深入浅出

    昨天呢,我们大家一起分享了jQuery中的样式选择器,那么今天我们就来看一下jQuery中的事件机制,其实,jQuery中的事件机制与JavaScript中的事件机制区别是不大的,只是,JavaScr ...

  3. 【初窥javascript奥秘之事件机制】论“点透”与“鬼点击”

    前言 最近好好的研究了一番移动设备的点击响应速度,期间不断的被自己坑,最后搞得焦头烂额,就是现在可能还有一些问题,但是过程中感觉自己成长不少, 最后居然感觉对javascript事件机制有了更好的认识 ...

  4. 【探讨】javascript事件机制底层实现原理

    前言 又到了扯淡时间了,我最近在思考javascript事件机制底层的实现,但是暂时没有勇气去看chrome源码,所以今天我来猜测一把 我们今天来猜一猜,探讨探讨,javascript底层事件机制是如 ...

  5. React事件机制 - 源码概览(下)

    上篇文档 React事件机制 - 源码概览(上)说到了事件执行阶段的构造合成事件部分,本文接着继续往下分析 批处理合成事件 入口是 runEventsInBatch // runEventsInBat ...

  6. 11.QT事件机制源码时序分析(下)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109182406,本文继续解析QCoreApplication::sendEvent和Q ...

  7. 10.QT事件机制源码时序分析(中)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109162220,上篇文章已经说过,在Ubuntu18.04,QT的事件机制实际上是采用 ...

  8. 9.QT事件机制源码时序分析(上)

    通过上两篇博客https://blog.csdn.net/Master_Cui/article/details/109093845和https://blog.csdn.net/Master_Cui/a ...

  9. Spring 与 Spring Boot 中的事件机制

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 温安适 来源 | https://my.osc ...

最新文章

  1. 从一个面试题看程序员的几项基本功
  2. 《深入理解Java虚拟机》笔记01 -- 运行时数据区
  3. python软件下载路径问题-mac上Python安装和修改Python默认路径遇到的问题
  4. Oracle内部错误:ORA-00600[2608]一例
  5. 在nodejs中的集成虹软人脸识别
  6. ElementUI dialog弹框 退出时重置表单数据
  7. Lfie has sweet
  8. 范式化设计和反范式化设计优缺点
  9. java的web项目资源访问规则
  10. docker build -t_在Docker环境构建、打包和运行Spring Boot应用
  11. mysql实例管理工具巨杉_使用_操作_MySQL实例_关系型数据库实例_文档中心_SequoiaDB巨杉数据库...
  12. Pandas 分割字符串
  13. JS实现购物车全选、不选、反选的功能(十七)
  14. dell台式计算机恢复出厂设置,戴尔电脑如何恢复出厂设置
  15. 【龙印】用M665和M666给三角洲3D打印机调平
  16. 排球分组循环交叉编排_巍山县举办气排球赛,看谁技高一筹!
  17. 干货|科技赋能财富硬核直播带货,助力宜信财富逆势增长
  18. 洗地机性价比高的是哪款?性价比高适合入手的洗地机介绍
  19. 八位计算机最小二进制,八位二进制补码最小值
  20. OpenCV笔记整理【直方图和掩模】

热门文章

  1. http --- cookie与会话跟踪
  2. 学习React的一知半解
  3. hashcat源码分析1
  4. 使用Firefox或Chrome的雇员表现更好不频繁跳槽
  5. sql2012一段时间无法连接报53错误
  6. linux定时任务启动
  7. Quickly Find/ Open a file in Visual Studio
  8. PCM接口详细介绍--TDM方式
  9. HALCON示例程序measure_circles.hdev测量圆的半径
  10. 【图像处理】——Python实现灰度特征提取