Contents

Hibernate 拦截器简介

1. 概述

在本次讨论中,我们将研究在Hibernate 的抽象关系映射实现中拦截操作的各种方法。

2. 定义 Hibernate 拦截器

Hibernate 拦截器是一个接口,它允许我们对 Hibernate 中的某些事件做出反应。

这些拦截器注册为回调,并提供 Hibernate 的会话和应用程序之间的通信链接。通过这样的回调,应用程序可以拦截核心 Hibernate 的操作,例如保存、更新、删除等。

定义拦截器有两种方式:

  1. 实现org.hibernate.Interceptor接口
  2. 扩展org.hibernate.EmptyInterceptor

2.1. 实现Interceptor接口

实现org.hibernate.Interceptor需要实现大约 14 个附带的方法。这些方法包括onLoad、onSave、onDelete、findDirty等等。

确保任何实现 Interceptor 接口的类都是可序列化的(实现 java.io.Serializable)也很重要。 一个典型的例子如下:

public class CustomInterceptorImpl implements Interceptor, Serializable {
    @Override
    public boolean onLoad(Object entity, Serializable id, 
      Object[] state, String[] propertyNames, Type[] types) 
      throws CallbackException {
        // ...
        return false;
    }
    // ...
    @Override
    public String onPrepareStatement(String sql) {
        // ...   
        return sql;
    }
}

如果没有特殊要求,强烈建议扩展EmptyInterceptor并仅覆盖所需的方法。

2.2. 扩展EmptyInterceptor

扩展org.hibernate.EmptyInterceptor类提供了一种更简单的定义拦截器的方法。我们现在只需要重写与我们要拦截的操作相关的方法。

例如,我们可以将CustomInterceptor定义为:

public class CustomInterceptor extends EmptyInterceptor {
}

而如果我们需要在执行数据保存操作之前拦截它们,我们需要重写onSave方法:

@Override
public boolean onSave(Object entity, Serializable id, 
  Object[] state, String[] propertyNames, Type[] types) {
    
    if (entity instanceof User) {
        logger.info(((User) entity).toString());
    }
    return super.onSave(entity, id, state, propertyNames, types);
}

注意这个实现如何简单地打印出实体——如果它是一个User

虽然可以返回truefalse值,但通过调用super.onSave()允许传播onSave事件是一个好习惯。

**另一个用例是为数据库交互提供审计跟踪。**我们可以使用*onFlushDirty()*方法来了解实体何时发生变化。

对于User对象,我们可以决定在User类型的实体发生更改时更新其lastModified日期属性。

这可以通过以下方式实现:

@Override
public boolean onFlushDirty(Object entity, Serializable id, 
  Object[] currentState, Object [] previousState,
  String[] propertyNames, Type[] types) {
    
    if (entity instanceof User) {
        ((User) entity).setLastModified(new Date());
        logger.info(((User) entity).toString());
    }
    return super.onFlushDirty(entity, id, currentState, 
      previousState, propertyNames, types);
}

其他事件如删除和加载(对象初始化)可以通过分别实现相应的onDeleteonLoad方法来拦截。

3.注册拦截器

Hibernate 拦截器可以注册为Session-scopedSessionFactory-scoped

3.1. getSessionWithInterceptor

一个Session范围的拦截器链接到一个特定的会话。它是在会话被定义或打开时创建的:

public static Session getSessionWithInterceptor(Interceptor interceptor) 
  throws IOException {
    return getSessionFactory().withOptions()
      .interceptor(interceptor).openSession();
}

在上面,我们显式地注册了一个带有特定休眠会话的拦截器。

3.2. SessionFactory范围的拦截器

在构建 SessionFactory 之前注册一个SessionFactory范围的拦截器。这通常通过SessionFactoryBuilder实例上的applyInterceptor方法完成:

ServiceRegistry serviceRegistry = configureServiceRegistry();
SessionFactory sessionFactory = getSessionFactoryBuilder(serviceRegistry)
  .applyInterceptor(new CustomInterceptor())
  .build();

需要注意的是,SessionFactory范围的拦截器将应用于所有会话。因此,我们需要注意不要存储特定于会话的状态——因为这个拦截器将被不同的会话同时使用。

对于特定于会话的行为,建议使用前面显示的不同拦截器显式打开会话。

对于SessionFactory范围的拦截器,我们自然需要确保它是线程安全的。这可以通过在属性文件中指定会话上下文来实现:

hibernate.current_session_context_class=org.hibernate.context.internal.ThreadLocalSessionContext

或者通过将其添加到我们的 XML 配置文件中:

<property name="hibernate.current_session_context_class">
    org.hibernate.context.internal.ThreadLocalSessionContext
</property>

此外,为了确保可串行化,SessionFactory范围的拦截器必须实现Serializable接口的readResolve方法。