我们使用 Hibernate(带有 JPA)和 Hibernate Envers 来保存对象的历史记录。Web应用程序运行许多线程,其中一些线程是由其他应用程序调用RMI方法创建的,其中一些是由应用程序本身创建的,还有一些是为了处理http请求而创建的(它们生成视图)。

我们还使用“在视图中打开会话”模式来管理会话,因此我们的 web.xml 包含:

<filter>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

数据库是使用DAO访问的,它们都有Spring注入的EntityManager。

@PersistenceContext
protected EntityManager em;

@PersistenceUnit
protected EntityManagerFactory emf;

在我们决定使用 Hibernate Envers 之前,一切都运行良好。当任何不是视图生成线程的线程运行代码来获取对象的旧版本时,会引发异常。

@Override
public O loadByRevision(Long revision, Long id) {
    @SuppressWarnings("unchecked")
    O object = (O) AuditReaderFactory.get(em).createQuery().forEntitiesAtRevision(getBaseClass(), revision.intValue())
            .add(AuditEntity.id().eq(id)).getSingleResult();
    return object;
}

线程“Scheduler”org.hibernate.SessionException 中的异常:会议已结束!at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:129) at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1776) at org.hibernate.envers.tools.query.QueryBuilder.toQuery(QueryBuilder 。 hibernate.envers.query.impl.abstractauditquery.getsingleresult(AbstrackAuditquery.java:110)(...)

当上面的代码由视图生成线程运行时,它可以正常工作。此外,DAO 中的非 envers 代码适用于每个线程。例如,下面的代码片段

@Override
public O load(Long id) {
    final O find = em.find(getBaseClass(), id);
    return find;
}

可以通过 RMI 线程运行而不会出现问题。

为什么非视图线程可以无异常地调用实体管理器上的方法,但不能将 Envers 的 AuditReaderFactory 与该实体管理器一起使用?我认为调用实体管理器上的方法可能会创建一个临时会话,但使用 Envers 时不会发生这种情况,是真的吗?

解决该问题的最佳方法是什么(以便可以从每个线程使用 AuditReaderFactory)?

有帮助吗?

解决方案

我们没有找到为什么在非ui线程中方法调用 EntityManagerFactory 有效,但方法调用 AuditReaderFactory 没有。无论如何,我们找到了解决问题的方法。

解决方案是用注释方法 @Transactional. 。如果在调用 AuditReaderFactory 之前调用链中的任何方法被标记为 @Transactional, 没有 SessionException 在非 UI 线程中。

事实证明,制作 loadByRevision 交易性还不够。如果该方法返回的对象包含延迟加载的持久包,则在外部访问它们 loadByRevision 方法作用域引起 LazyInitializationException (没有会议)。

最终的解决方案是确保如果任何线程想要从数据库加载一些数据,所有加载(获取对象和访问延迟加载的集合)都将在一个注释为的方法内完成 @Transactional.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top