使用Spring Boot和Spring Data JPA的Hibernate拦截器或监听器

9

在保存一个对象的子集合之前(cascade = all),我希望进行一些检查。我使用了Spring Boot和Spring Data JPA,想知道哪种方法最好:Hibernate监听器还是拦截器?每种方法的优缺点是什么?你有没有一个最佳实践的例子?

我以前用过配置在XML中的Hibernate监听器,如下所示:

    <property name="eventListeners">
        <map>
            <entry key="post-update">
                <list>
                    <ref bean="myListener" />
                </list>
            </entry>
        </map>
    </property>

对于会话工厂(较旧的项目),我原本使用的是XML配置。但现在大多数配置都是使用注释来实现(因为Spring Boot),而且我希望保持配置尽可能简单和轻量化,因此也许一个拦截器会是更好的解决方案。

谢谢。

2个回答

45

我自己对此进行了很多调查,并想与大家分享我的经验(底部包含有用的链接)。

拦截器

要使用拦截器,您需要扩展org.hibernate.EmptyInterceptor类并覆盖您想要拦截的方法。 在您的情况下,您可能需要使用onSave(...)方法。

package foo.bar;

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import java.io.Serializable;

public class MyInterceptor extends EmptyInterceptor {
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        // do your checks here
        return false;
    }
}

您需要在Spring/Hibernate中注册您的拦截器。 您可以在您的application.properties或application.yml中完成此操作。

spring:
  jpa:
    properties:
      hibernate.ejb.interceptor: foo.bar.MyInterceptor

使用拦截器的优点是它(可能)需要的代码较少,并且配置相对简单。 缺点是你只能在整个应用程序中使用一个拦截器,而且与API一起使用可能会令人困惑。

事件监听器

对于事件,您可以实现Hibernate的org.hibernate.event.spi.*Listener接口之一。 在您的情况下,您可能需要org.hibernate.event.spi.PreInsertEventListener

您必须在EventListenerRegistry中注册您的事件。 为此,您可以将您的类作为@Component,将EntityManagerFactory自动装配到您的类中,并创建一个@PostConstruct方法来注册您的类。

package foo.bar;

import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreInsertEvent;
import org.hibernate.event.spi.PreInsertEventListener;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;

@Component
public class MyEventListener implements PreInsertEventListener {
    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @PostConstruct
    private void init() {
        SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
    }

    @Override
    public boolean onPreInsert(PreInsertEvent preInsertEvent) {
        // do your checks here
        return false;
    }
}

监听器的优点在于你可以有任意多个,API比拦截器更好用,代码和配置都在同一个地方。

缺点是配置较长且复杂



Hibernate支持会话范围的拦截器和应用程序范围的拦截器,您需要使用此属性而不是“hibernate.ejb.interceptor.session_scoped”。 - cyberoblivion
2
太棒了,我找了很多指导,就像你这样好的指导。 - Michael Hegner
1
太棒了,解释得非常好! - Onkaar Singh

5

你好,首先可以查看这篇文章:https://www.baeldung.com/database-auditing-jpa ,其中详细解释了各种选项。

个人建议使用Hibernate拦截器,易于使用和理解。根据项目的复杂性,在大多数情况下都可以胜任。

要在应用程序中配置它,只需在application.properties中添加:spring.jpa.properties.hibernate.ejb.interceptor = path.to.interceptor。拦截器本身应该是一个@Component

只要拦截器没有实际使用任何bean,否则就会变得更加复杂,但我很乐意提供解决方案。

别忘了在application-test.properties中添加一个EmptyInterceptor以不使用日志系统(或用于其他目的)来测试(这对于测试并不是非常有帮助)。

希望这对您有所帮助。

最后要注意的是:始终更新您的Spring / Hibernate版本(尽可能使用最新版本),您将看到大多数代码将变得不必要,因为较新的版本试图尽量减少配置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接