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 提供了非入侵性的回调拦截机制,使我们可以方便而且优雅的实现一些持久化操作的特殊需求。
 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值