Hibernate Interceptors – Hibernate拦截器

最后修改: 2017年 12月 29日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this discussion, we’ll look at various ways of intercepting operations within Hibernate’s abstracted relational mapping implementation.

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

2. Defining Hibernate Interceptors

2.定义Hibernate拦截器

The Hibernate Interceptor is an interface that allows us to react to certain events within Hibernate.

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

These interceptors are registered as callbacks and provide communication links between Hibernate’s session and application. With such a callback, an application can intercept core Hibernate’s operations such as save, update, delete, etc.

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

There are two ways of defining interceptors:

有两种定义拦截器的方法。

  1. implementing the org.hibernate.Interceptor interface
  2. extending the org.hibernate.EmptyInterceptor class

2.1. Implementing an Interceptor Interface

2.1.实现一个拦截器接口

Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods. These methods include onLoad, onSave, onDelete, findDirty, and a few more.

实现org.hibernate.Interceptor需要实现大约14个附带的方法。这些方法包括onLoad, onSave, onDelete, findDirty,和其他一些方法。

It’s also important to ensure that any class that implements Interceptor interface is serializable (implements java.io.Serializable).

确保任何实现拦截器接口的类是可序列化的(实现java.io.Serializable)也很重要。

A typical example would look like:

一个典型的例子是这样的。

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;
    }

}

If there are no special requirements, extending the EmptyInterceptor class and only overriding the required methods is highly recommended.

如果没有特殊要求,扩展EmptyInterceptor类,只重写所需的方法,这是强烈建议的。

2.2. Extending EmptyInterceptor

2.2.扩展EmptyInterceptor

Extending the org.hibernate.EmptyInterceptor class provides an easier way of defining an interceptor. We now only need to override the methods that relate to the operation we want to intercept.

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

For example, we can define our CustomInterceptor as:

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

public class CustomInterceptor extends EmptyInterceptor {
}

And if we need to intercept data saving operations before they are executed, we need to override onSave method:

而如果我们需要在执行数据保存操作之前拦截它们,我们需要覆盖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);
}

Notice how this implementation simply prints out the entity – if it’s a User.

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

While it’s possible to return a value of true or false, it’s a good practice to allow propagation of onSave event by invoking super.onSave().

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

Another use-case would be providing an audit trail for database interactions. We can use the onFlushDirty() method to know when an entity changes.

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

For the User object, we can decide to update its lastModified date property whenever changes on entities of type User happen.

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

This can be achieved with:

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

@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);
}

Other events such as delete and load (object initialization) can be intercepted by implementing the corresponding onDelete and onLoad methods respectively.

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

3. Registering Interceptors

3.注册拦截器

A Hibernate interceptor can either be registered as Session-scoped or SessionFactory-scoped.

Hibernate拦截器可以被注册为Session范围或SessionFactory范围

3.1. Session-scoped Interceptor

3.1.会话范围的拦截器

A Session-scoped interceptor is linked to a specific session. It’s created when the session is being defined or opened as:

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

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

In the above, we explicitly registered an interceptor with a particular hibernate session.

在上面,我们明确地将一个拦截器与一个特定的hibernate会话注册在一起。

3.2. SessionFactory-scoped Interceptor

3.2.SessionFactory范围的Interceptor

A SessionFactory-scoped interceptor is registered before building a SessionFactory. This is typically done through the applyInterceptor method on a SessionFactoryBuilder instance:

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

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

It’s important to note that a SessionFactory-scoped interceptor will be applied to all sessions. Hence, we need to be careful not to store session specific state – as this interceptor will be used by different sessions concurrently.

值得注意的是,一个SessionFactory-范围的拦截器将被应用于所有会话。因此,我们需要注意不要存储会话的具体状态–因为这个拦截器会被不同的会话同时使用。

For a session specific behavior, it’s recommended to explicitly open a session with a different interceptor as earlier shown.

对于一个会话的具体行为,建议像前面显示的那样,用不同的拦截器显式地打开一个会话。

For SessionFactory-scoped interceptors, we naturally need to ensure that it’s thread-safe. This can be achieved by specifying a session context in the properties file:

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

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

Or by adding this to our XML configuration file:

或者通过添加这个到我们的XML配置文件中。

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

Also, to ensure serializability, SessionFactory-scoped interceptors must implement the readResolve method of the Serializable interface.

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

4. Conclusion

4.结论

We’ve seen how to define and register Hibernate interceptors either as Session-scoped or SessionFactory-scoped. In either case, we must ensure that the interceptors are serializable especially if we want a serializable session.

我们已经看到了如何定义和注册Hibernate拦截器,无论是Session范围还是SessionFactory范围。无论哪种情况,我们都必须确保拦截器是可序列化的,特别是如果我们想要一个可序列化的会话。

Other alternatives to interceptors include Hibernate Events and JPA Callbacks.

拦截器的其他替代品包括Hibernate Events和JPA Callbacks。

And, as always, you can check out the complete source code over on Github.

而且,您可以一如既往地在Github上查看完整的源代码