首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Hibernate中的回调与阻截机制

2013-11-09 
Hibernate中的回调与拦截机制在某些情况下,我们需要对实体的CURD操作进行捕获并执行一些操作,这可以通过数

Hibernate中的回调与拦截机制

在某些情况下,我们需要对实体的CURD操作进行捕获并执行一些操作,这可以通过数据库触发器来实现,但是正如我们上一节中所分析的,由于触发器的执行对Hibernate Session是透明的,因此会带来很多问题(参见上一节)。为此Hibernate提供了一些专门用于捕获监听实体CURD操作的接口,通过这些接口可以实现类似触发器的功能,能够在实体发生CURD操作时捕获事件,并且执行相应的动作逻辑。在Hibernate中这些接口是:Lifecycle,Validatable,Interceptor,下面我们就分别讲解怎样通过这些接口,实现回调拦截的技术细节。

A、Lifecycle与Validatable

?在Hibernate中Lifecycle接口定义如下:

public interface Lifecycle{

?/**

?? 在实体对象执行save/insert操作之前触发

?**/

?public boolean onSave(Session session) throws CallbackException;

?/**

?? 在session.update()执行之前触发

?**/

?public boolean onUpdate(Session session) throws CallbackException;

?/**

?在实体对象执行delete操作之前触发

**/

public boolean onDelete(Session session) throws CallbackException;

/**

?在实体对象加载之后触发

**/

public void onLoad(Session session) throws CallbackException;

}

实体对象可以实现Lifecycle接口,来获得在持久化阶段捕获CURD事件,并执行相应动作的能如下所示:

public class User implements Serializable,Lifecycle{

?public boolean onSave(Session s) throws CallbackException{

……

return false;

……

?}

?public boolean onUpdate(Session s) throws CallbackException{

……

return true;

……

?}

?public boolean onDelete(Session s) throws CallbackException{

……

return false;

……

?}

?public boolean onLoad(Session s) throws CallbackException{

……

?}

}

对于onSave,onUpdate,onDelete方法,如果返回true则意味着需要终止执行对应的操作过程。如果在运行时抛出CallbackException,对应的操作也会被终止。

注意在接口中对应的方法中,不要去通过方法的Session参数执行持久化操作,在这些方法中Session无法正常使用,如果必须要执行一些持久化操作,那么需要进行特殊的处理,我们将在Interceptor部分详细讲解。

Hibernate中还定义了Validatable接口,该接口定义如下:

public interface Validatable{

?public void validate() throws ValidationFailure;

}

Validatable接口是用来实现数据验证的,实体类实现Validatable接口,并在接口的validate方法中实现数据验证逻辑,以保证数据输入的合法性。validate方法将会在实体对象持久化前得到调用进行数据验证,与Lifecycle接口中的方法不同,Validatable.validate()方法在实体生命周期中可能被多次调用,因此此方法应该仅限于数据合法性的验证,而不应该实现业务逻辑的验证。

B、Interceptor:

以上是Hibernate提供的Lifecycle接口和Validatable接口,以及使用方法,这两个方法定义了一种自然的回调机制,但是如我们所见,如果想实现对实体的回调拦截,那么相应的实体对象必须实现这两个Hibernate原生接口,这就使代码的可移植性大大下降,因为此时实体类已经不再是一个POJO了,Hibernate的那些天才的设计者们也已经意识到了这个问题,所以又提供了Interceptor接口,为持久化事件的捕获和处理提供了一个非入侵性的解决方案,Interceptor接口通过设置注入来实现持久化事件的捕获和处理,这是典型的IOC(控制反转)设计思想。下面我们就讲解Interceptor接口的技术细节和使用方法。

Hibernate中的Interceptor接口定义如下:

public interface Interceptor{

?//对象初始化之前调用,这时实体对象刚刚被创建,各个属性还都为null,如果在这个方法中修改了实体对象的数据,那么返回true,否则返回null.

?public boolean onLoad(Object entity,Serializable id,Object[] state,

String[] propertyNames,Type[] types) throws CallbackException;

??? ??//Session.flush()在进行脏数据检查时,如果发现实体对象数据已脏,就调用此方法

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

String[] propertyNames,Type[] types) throws CallbackException;

?//实体对象被保存前调用,如果在这个方法中修改了实体对象的数据,那么返回true,否则返回null.

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

String[] propertyNames,Type[] types) throws CallbackException;

???? //通过Session删除一个实体对象前调用

?public boolean onDelete(Object entity,Serializable id,Object[] state,

String[] propertyNames,Type[] types) throws CallbackException;

???? //Session执行flush()之前调用

public boolean preFlush(Iterator entities) throws CallbackException;

???? //Session执行flush()之后,所有的SQL语句都执行完毕后调用

public boolean postFlush(Iterator entities) throws CallbackException;

???? //当执行saveOrUpdate方法时调用,判断实体对象是否已经保存

???? public Boolean isUnsaved(Object entity);

???? //执行Session.flush()方法时,调用此方法判断该对象是否为脏对象,这提供了脏数据检查的另一个回调拦截机制

public int[] findDirty(Object entity,Serializable id,Object[] state,

String[] propertyNames,Type[] types) throws CallbackException;

???? //当Session构造实体类实例前调用,如果返回null,Hibernate会按照默认方式构造实体类对象实例

public Object findDirty(Class clazz,Serializable id) throws CallbackException;

}

Intercepter不需要实体对象来实现,而是通过开发人员定义一个实现Interceptor接口的类,然后在创建Hibernate Session时,通过将Interceptor对象设置进所创建的Session,这样通过这个Session来操作的实体对象,就都会具有对持久化动作的回调拦截能力。在Hibernate中Interceptor对象共有两种用法,如下所述:

1、??????????????? SessionFactory.openSession(Interceptor):为每个Session实例分配一个拦截Interceptor,这个拦截接口对象,存放在Session范围内,为每个Session实例所专用。

2、??????????????? Configuration.setInterceptor(Interceptor):为SessionFactory实例分配一个Interceptor实例,这个Interceptor实例存放在SessionFactory范围内,被每个Session实例所共享。

A、Interceptor的典型应用:

下面我实现一个利用Interceptor接口实现日志数据稽核的功能,所谓日志数据稽核就是针对一些关键操作进行记录,以便作为业务跟踪的基础依据。

首先定义用于记录操作的实体:

public class AudiLog implements Serializable{

?private String id;

?private String user;

?private String action;

?private String entityName;

?private String comment;

?private Long logtime;

?…getter/setter…

}

接下来定义Interceptor接口的实现类和用于持久化操作的AuditDAO类:

package com.lbs.apps.unemployment.subsidy.beforeinfoimport.util;

import net.sf.hibernate.Session;

import net.sf.hibernate.Interceptor;

import Java.io.Serializable;

import net.sf.hibernate.type.Type;

import net.sf.hibernate.HibernateException;

import Java.util.Iterator;

import Java.util.Set;

import Java.util.HashSet;

import com.neusoft.entity.User;

public class MyInterceptor implements Interceptor{

?private Set insertset=new HashSet();

?private Set updateset=new HashSet();

?private Session session;

?private String userID;

?public void setSession(Session session){

??? this.session=session

 }

 public void setUserID(String id){

?? this.userID=id;

?}

?public boolean onLoad(Object object, Serializable serializable,

??????????????????????? Object[] objectArray, String[] stringArray,

??????????????????????? Type[] typeArray) {

??? return false;

?}

?public boolean onFlushDirty(Object object, Serializable serializable,

????????????????????????????? Object[] objectArray, Object[] objectArray3,

????????????????????????????? String[] stringArray, Type[] typeArray) {

??? if(object instanceof User){

????? insertset.add(object);

??? }

??? return false;

?}

?public boolean onSave(Object object, Serializable serializable,

??????????????????????? Object[] objectArray, String[] stringArray,

??????????????????????? Type[] typeArray) {

??? if(object instanceof User){

????? updateset.add(object);

??? }

??? return false;

?}

?public void onDelete(Object object, Serializable serializable,

?????????????????????? Object[] objectArray, String[] stringArray,

?????????????????????? Type[] typeArray) {

?}

?public void preFlush(Iterator iterator) {

?}

?public void postFlush(Iterator iterator) {

try{

?if(insertset.size()>0){

?? AuditDAO.dolog(“insert”,userID,inserset,session.connection);

?}

?if(updateset.size()>0){

?? AuditDAO.dolog(“update”,userID,updateset,session.connection);

?}

}catch(HibernateException he){

?he.printStackTrace();

}

?}

?public Boolean isUnsaved(Object object) {

??? return null;

?}

?public int[] findDirty(Object object, Serializable serializable,

???????????????????????? Object[] objectArray, Object[] objectArray3,

???????????????????????? String[] stringArray, Type[] typeArray) {

??? return null;

?}

?public Object instantiate(Class class0, Serializable serializable) {

??? return "";

?}

}

public class AuditDAO{

?public static void doLog(String action,String userID,Set modifySet,Connection connection){

???? Session tempsession=HibernateUtil.getSessionFactory().openSession(connection);

???? try{

?????? Iterator it=modifyset.iterator();

?????? while(it.hasNext()){

???????? User user=(User)it.next();

???????? AudiLog log=new AudiLog();

???????? log.setUserID(userID);

???????? log.setAction(action);

???????? log.setComment(user.toString());

???????? log.setLogTime(new Long(Calendar.getInstance().getTime().getTime()));

???????? tempsession.save(log);

?????? }

???? }catch(Exception e){

?????? throw new CallbackException(e);

???? }finally{

?????? try{

?? ??????tempsesson.close();

?????? }catch(HibernateException he){

???????? throw new CallbackException(he);

?????? }

???? }

?}

}

最后看一下业务逻辑主程序:

SessionFactory sessionfactory=config.buildSessionFactory();

MyInterceptor it=new MyInterceptor();

session=sessionfactory().openSession(it);

it.setUserID(“currentUser”);

it.setSession(session);

User user=new User();

user.setName(“zx”);

Transaction tx=session.beginTransaction();

session.save(user);

tx.commit();

session.close();

以上示例代码中,在创建Session时,设置Interceptor实例对象,当执行到session.save(user)前,会触发onSave()方法,当执行tx.commit()时,会执行flush(),在执行该方法后会触发postFlush()方法,这个方法通过AuditDAO进行持久化保存业务日志,在这个类中的红色部分时有关持久化操作部分,我们并没有使用原有的Session实例,这是因为要避免Session内部状态混乱,因此我们依托当前Session的JDBC Connection创建了一个临时Session用于保存操作记录,在这个持久化操作中没有启动事务,这是因为临时Session中的JDBC Connection是与外围调用Interceptor的Session共享,而事务已经在外围Session的JDBC Connection上启动。这是在拦截方法中进行持久化操作的标准方法。总之Interceptor提供了非入侵性的回调拦截机制,使我们可以方便而且优雅的实现一些持久化操作的特殊需求。

热点排行