Hibernate拦截器与监听器

来源:互联网 发布:linux 用户配额 编辑:程序博客网 时间:2024/05/25 20:00
  拦截器(Intercept):与Struts2的拦截器机制基本一样,都是一个操作穿过一层层拦截器,每穿过一个拦截器就会触发相应拦截器的事件做预处理或善后处理。

  监听器(Listener):其实功能与拦截器是相似的,但它实现原理不同,它是为每一个事件注册一个或多个监听器,一旦事件发生,则事件源通知所有监听该事件的监听器,然后监听器处理通知(观察者模式)。

-------------------------------------------------------------------------------------

拦截器

  Hibernate为我们提供了实现拦截器的接口org.hibernate.Interceptor,它里面提供了许多拦截事件。通常不需要实现这个接口,因为我们实现自己的拦截器不可能每一个事件都是必须的。所以Hibernate为我们提供了org.hibernate.Interceptor接口的一个空实现类org.hibernate.EmptyIntercept,通常情况下我们只需继承这个空实现类,Override需要的事件方法即可。

拦截器的工作原理简易示意图:


设置拦截器后,相应的操作都会先穿过一层层相应的拦截器,让拦截器执行预处理或善后处理。

拦截器使用实例:

创建拦截器:

package customIntercept;import java.io.Serializable;import org.hibernate.EmptyInterceptor;import org.hibernate.type.Type;public class MyIntercept extends EmptyInterceptor {/** *  */private static final long serialVersionUID = 5594120787569990595L;/*     * entity - POJO对象     * id - POJO对象的主键     * state - POJO对象的每一个属性所组成的集合(除了ID)     * propertyNames - POJO对象的每一个属性名字组成的集合(除了ID)     * types - POJO对象的每一个属性类型所对应的Hibernate类型组成的集合(除了ID)     */@Overridepublic boolean onSave(Object entity, Serializable id, Object[] state,String[] propertyNames, Type[] types) {System.out.println("-->onSave<--");for(int index=0; index<propertyNames.length; index++){if("sex".equals(propertyNames[index])){state[index] = "男";return true;}}return false;}}

测试方法:

@Testpublic void testCustomIntercept(){Session session = HibernateSessionFactory.getSessionFactory().openSession(new MyIntercept());User user = new User();user.setAge(20);user.setName("张三");Transaction tx = session.beginTransaction();session.save(user);tx.commit();}

测试方法中没有显式的设置User的性别,执行测试方法,查看数据库信息:


控制台打印:


可以发现保存的时候执行了过滤器,数据库表user的sex列也被赋值了,说明该赋值是拦截器完成的。

使用拦截器需要注意拦截器返回值,我以前一直以为拦截器的返回值会控制一个操作是否可以继续,通过实验发现,即使返回false操作也会继续执行的,只是返回false的话,拦截器的所有设置都是无效的,不会反应到数据库中。

返回false测试:

package customIntercept;import java.io.Serializable;import org.hibernate.EmptyInterceptor;import org.hibernate.type.Type;public class MyIntercept extends EmptyInterceptor {/** *  */private static final long serialVersionUID = 5594120787569990595L;/*     * entity - POJO对象     * id - POJO对象的主键     * state - POJO对象的每一个属性所组成的集合(除了ID)     * propertyNames - POJO对象的每一个属性名字组成的集合(除了ID)     * types - POJO对象的每一个属性类型所对应的Hibernate类型组成的集合(除了ID)     */@Overridepublic boolean onSave(Object entity, Serializable id, Object[] state,String[] propertyNames, Type[] types) {System.out.println("-->onSave<--");for(int index=0; index<propertyNames.length; index++){if("sex".equals(propertyNames[index])){state[index] = "男";//return true;}}return false;}}

测试方法:

@Testpublic void testCustomInterceptfalse(){Session session = HibernateSessionFactory.getSessionFactory().openSession(new MyIntercept());User user = new User();user.setAge(21);user.setName("李四");Transaction tx = session.beginTransaction();session.save(user);tx.commit();}

查看数据库:


控制台打印:


可以发现数据依然保存到了数据库,但是拦截器的操作没有反映到数据库中,说明拦截器的操作时无效的。

控制台输出的sql语句与返回true的时候相比多了一条update语句,说明拦截器的操作会直接反映到数据库,但是如果拦截器返回的false,Hibernate会产生一条update语句将拦截器的操作回滚掉。

Hibernate拦截器可以分为两种:全局拦截器(SessionFactory范围内的)和局部拦截器(Session范围内的)

全局拦截器的设置方式:new Configuration().setInterceptor( new AuditInterceptor() );

局部拦截器的设置方式:Session session = sf.openSession( new AuditInterceptor() );

  SessionFactory范围内的拦截器要通过Configuration中注册,而这必须在创建SessionFactory之前。在这种情况下,给出的拦截器会被这个SessionFactory所打开的所有session使用了;除非session打开时明确指明了使用的拦截器。SessionFactory范围内的拦截器,必须是线程安全的,因为多个session可能并发使用这个拦截器,要因此小心不要保存与session相关的状态。

------------------------------------------------------------------------------------

事件监听器

  基本上,Session接口的每个方法都有相对应的事件。比如 LoadEvent,FlushEvent,等等(查阅XML配置文件的DTD,以及org.hibernate.event包来获得所有已定义的事件的列表)。当某个方法被调用时,Hibernate Session会生成一个相对应的事件并激活所有配置好的事件监听器。系统预设的监听器实现的处理过程就是被监听的方法要做的(被监听的方法所做的其实仅仅是激活监听器, “实际”的工作是由监听器完成的)。不过,你可以自由地选择实现一个自己定制的监听器(比如,实现并注册用来处理处理LoadEvent的LoadEventListener接口), 来负责处理所有的调用Session的load()方法的请求。

在定义自己的事件监听器时,其实不需要实现XXXListener接口,Hibernate为了方便我们定义事件监听器,已经为每个事件监听器接口实提供了一个默认的实现。在org.hibernate.event.def包下面可以找到Hibernate为我们提供的默认实现,我们只需要继承这些默认实现,在其基础上添加我们自定义的功能即可。

事件监听器的简单示意图:


当某个方法被调用时,Hibernate Session会生成一个相对应的事件并激活所有配置好的事件监听器。

事件监听器使用示例:

事件监听器代码:

package customListener;import org.hibernate.event.SaveOrUpdateEvent;import org.hibernate.event.def.DefaultSaveOrUpdateEventListener;import domain.User;public class MyListener extends DefaultSaveOrUpdateEventListener {/** *  */private static final long serialVersionUID = 9057325840987859665L;@Overridepublic void onSaveOrUpdate(SaveOrUpdateEvent event) {System.out.println("-->执行onSaveOrUpdate<--");User user;if(event.getObject() instanceof User){user = (User) event.getObject();user.setSex("男");}/*一定要调用父类提供的功能,不然就和继承接口一样了*/super.onSaveOrUpdate(event);}}

如果监听器不执行父类的方法super.onSaveOrUpdate(event);则hibernate操作不会反应到数据库中。

通过hibernate.cfg.xml配置文件将事件监听器配置到Hibernate中:

第一种配置方式:

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration>    <session-factory>     <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>     <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate3_6</property>     <property name="hibernate.connection.username">root</property>     <property name="hibernate.connection.password">root</property>     <property name="hibernate.show_sql">true</property>     <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>     <property name="hibernate.hbm2ddl.auto">update</property>       <mapping resource="domain/user.hbm.xml" />     <event type="save-update">     <listener class="customListener.MyListener"  />     </event>         </session-factory></hibernate-configuration>

第二种配置方式:

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration>    <session-factory>     <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>     <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate3_6</property>     <property name="hibernate.connection.username">root</property>     <property name="hibernate.connection.password">root</property>     <property name="hibernate.show_sql">true</property>     <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>     <property name="hibernate.hbm2ddl.auto">update</property>       <mapping resource="domain/user.hbm.xml" />     <listener class="customListener.MyListener"  type="save-update" />         </session-factory></hibernate-configuration>

注:监听器需要配置在mapping标签后面,不然会报错。

两种配置方式产生的效果都是一样的。只是一个以"事件"为主,一个以"监听器"为主。type是指定监听事件的类型,class指定监听器的实现类,一个事件可以有多个监听器。type有许多取值,下表列出了所有type的值:


上面列表每一个选项对应着一个特定的事件。

一个事件可以有多个事件监听器,如果自定义监听器里面没有执行父类的方法,而又想监听器操作反应到数据库中,那么可以在配置的时候加上默认监听器,例如:

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration>    <session-factory>     <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>     <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate3_6</property>     <property name="hibernate.connection.username">root</property>     <property name="hibernate.connection.password">root</property>     <property name="hibernate.show_sql">true</property>     <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>     <property name="hibernate.hbm2ddl.auto">update</property>       <mapping resource="domain/user.hbm.xml" />     <event type="save-update">     <listener class="customListener.MyListener"  />     <listener class="org.hibernate.event.def.DefaultSaveOrUpdateEventListener" />     </event>              </session-factory></hibernate-configuration>



测试方法代码:

@Testpublic void testCustomListener(){Session session = HibernateSessionFactory.getSession();System.out.println(session);User user = new User();user.setAge(50);user.setName("李四");Transaction tx = session.beginTransaction();session.saveOrUpdate(user);tx.commit();}


查看数据库:


控制台打印:


可以发现在测试方法中,没有显示指定user的性别,但是在数据库中user表的sex列被赋值,说明监听器起作用了。

使用事件监听器需要注意父类行为的顺序,例如:

@Overridepublic void onSaveOrUpdate(SaveOrUpdateEvent event) {System.out.println("-->执行onSaveOrUpdate<--");User user;if(event.getObject() instanceof User){user = (User) event.getObject();user.setSex("男");}/*一定要调用父类提供的功能,不然就和继承接口一样了*/super.onSaveOrUpdate(event);}


@Overridepublic void onSaveOrUpdate(SaveOrUpdateEvent event) {super.onSaveOrUpdate(event);System.out.println("-->执行onSaveOrUpdate<--");User user;if(event.getObject() instanceof User){user = (User) event.getObject();user.setSex("男");}}

虽然两个监听器执行的结果是一样的,但是第二种情况会多产生一条update语句,就是先把值写入到数据库,然后hibernate在产生一条update语句对sex列进行赋值,而如果使用第一种方法,是先设置user的sex属性,然后在一次性的写入到数据库中。

通过编程方式将监听器注册到hibernate中:

监听器类不变,hibernate配置文件跟不配置监听器一样配置:

myhibernate.cfg.xml代码:

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration>    <session-factory>     <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>     <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1/hibernate3_6</property>     <property name="hibernate.connection.username">root</property>     <property name="hibernate.connection.password">root</property>     <property name="hibernate.show_sql">true</property>     <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>     <property name="hibernate.hbm2ddl.auto">update</property> <property name="current_session_context_class">thread</property>     <mapping resource="domain/user.hbm.xml" />    </session-factory></hibernate-configuration>

测试方法代码:

@Testpublic void testCustomListener2(){SaveOrUpdateEventListener[] event = {new MyListener()};Configuration cfg = new Configuration().configure("/myhibernate.cfg.xml");cfg.getEventListeners().setSaveOrUpdateEventListeners(event);Session session = cfg.buildSessionFactory().getCurrentSession();System.out.println(session);User user = new User();user.setAge(50);user.setName("李四");Transaction tx = session.beginTransaction();session.saveOrUpdate(user);tx.commit();}
执行结果跟使用xml配置的结果一样。

  通过在XML配置文件声明而注册的监听器不能共享实例。如果在多个<listener/>节点中使用 了相同的类的名字,则每一个引用都将会产生一个独立的实例。如果你需要在多个监听器类型之间共享 监听器的实例,则你必须使用编程的方式来进行注册。 

原创粉丝点击