在Spring Boot应用程序中如何访问AuditReaderFactory?

12

我正在使用Spring Boot和Spring Data JPA。我还使用Hibernate Envers,并且需要访问AuditReaderFactory,以便可以编写审计查询。

由于这是Spring Boot和Spring Data JPA,因此一切都会自动配置。因此,当我这样做时,

@Autowired
AuditReaderFactory auditReaderFactory;

它不起作用。我收到了以下错误。

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.hibernate.envers.AuditReaderFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency

我该如何在我的存储库类中正确引用AuditReaderFactory?

5个回答

12

创建配置类,例如AuditConfiguration.java:

import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManagerFactory;

@Configuration
public class AuditConfiguration {

    private final EntityManagerFactory entityManagerFactory;

    AuditConfiguration(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Bean
    AuditReader auditReader() {
        return AuditReaderFactory.get(entityManagerFactory.createEntityManager());
    }
}

之后,您可以在组件类中自动装配AuditReader


1
您的答案将导致创建类似单例的Bean,这可能导致连接关闭不正确,从而完全停止服务以使其无法从数据库中获取任何内容(类似于此问题:https://dev59.com/o34QtIcB2Jgan1znweT-)。正确的答案是由@Guilio Pulina提供的。 - Dmitriy

6
在我看来,@deniss-s的答案(得票最高的答案)是不正确的,因为EntityManager不能在事务的上下文之外被重用(请参见https://dev59.com/GWox5IYBdhLWcg3wLhii#9375891),而该解决方案将其用作单例。
正确检索AuditReader的方法如下:
public class AuditRepository {

    @PersistenceContext
    private EntityManager entityManager;

    private AuditReader getAuditReader() {
        return AuditReaderFactory.get(entityManager);
    }
 
     public Optional<T> getRevision() {
         final AuditReader auditReader = getAuditReader();
         ...
    }

}

编辑:我刚意识到@deniss-s更新了他的答案(可能是在阅读我的答案后),但他的答案仍然是不正确的:AuditReader仍然被创建为单例,而每次必须创建一个新实例。


1
这是正确的答案!帮了很大的忙。谢谢你! - Dmitriy

5

针对上面的答案和相关问题,我发现以下解决方案可行。(如果可以直接将读取器自动连接起来会更好)

@Autowired
private EntityManagerFactory factory;

public void stuff() {
    AuditReader audit = AuditReaderFactory.get(factory.createEntityManager());
}

我认为使用@PersistenceContext注解将EntityManager进行连接会更清晰。 - Giulio Pulina

2
AuditReaderFactory只有两个静态方法。你能否自动装配一个SessionFactory对象或者你的EntityMananger呢?看起来这两个都可以给你想要的东西,即访问AuditReader。
AuditReaderFactory.get(sessionFactory.getCurrentSession())

编辑:如果需要,这篇文章提供了关于如何处理SessionFactory的详细信息。


以下链接中,自动装配 SessionFactory 失败了;虽然可以创建和保存实体,但无法获得 AuditReaderFactory:(Spring 1.5、Hibernate 5.2.1) org.hibernate.HibernateException: No CurrentSessionContext configured! - Jay

1

我无法创建评论,所以我将复制Giulio Pulina的答案。他的答案确实比那些得到更多赞的答案更正确。其他答案也可以工作,但只要与数据库有连接。如果与数据库失去连接(重新启动带有数据库的容器),AuditReader将变得无法操作。因此,在每个请求之前创建AuditReader的当前响应是最可取的。

正确检索AuditReader的方法如下:

public class AuditRepository {

    @PersistenceContext
    private EntityManager entityManager;

    private AuditReader getAuditReader() {
        return AuditReaderFactory.get(entityManager);
    }
 
     public Optional<T> getRevision() {
         final AuditReader auditReader = getAuditReader();
         ...
    }
}

1
使用具有有限连接TTL的连接池时,可能会发生连接丢失的情况(而且肯定会发生)。在接受答案中提出的配置中,AuditReader是单例模式(由应用程序的所有线程共享),并且绑定到池中的特定数据库连接。当应用程序一段时间内没有访问(例如,在夜间),数据库连接将过期,但仍然被AuditReader引用:这将使AuditReader无法正常运行并抛出大量异常。 - Giulio Pulina

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