Мы используем Hibernate (с JPA) и Hibernate Envers для сохранения истории объектов. Веб-приложение запускает много потоков, некоторые из них создаются с помощью вызова метода RMI из других приложений, некоторые из них создаются самим приложением, а некоторые из них создаются для обработки HTTP-запросов (они генерируют представления).Spring + Hibernate + Envers + многопоточность - сеанс закрыт
Мы также используем Open Session в View шаблон для управления сессий, поэтому наша 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, все они EntityManagers впрыскивается весной.
@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;
}
Исключение в потоке org.hibernate.SessionException "Планировщик": Сессия закрыта! на org.hibernate.internal.AbstractSessionImpl.errorIfClosed (AbstractSessionImpl.java:129) на org.hibernate.internal.SessionImpl.createQuery (SessionImpl.java:1776) на org.hibernate.envers.tools.query. QueryBuilder.toQuery (QueryBuilder.java:226) на org.hibernate.envers.query.impl.AbstractAuditQuery.buildQuery (AbstractAuditQuery.java:92) на org.hibernate.envers.query.impl.EntitiesAtRevisionQuery.list (EntitiesAtRevisionQuery.java:108) на org.hibernate.envers.query.impl.AbstractAuditQuery.getSingleResult (AbstractAuditQuery.java:110) (...)
Когда код, приведенный выше, запускается потоком, создающим вид, он отлично работает. Кроме того, код non-envers в DAO отлично работает для каждого потока. Например, фрагмент ниже
@Override
public O load(Long id) {
final O find = em.find(getBaseClass(), id);
return find;
}
может работать от нитей RMI без проблем.
Почему нельзя игнорировать потоки методов вызова в менеджере сущностей без исключений, но не может использовать Envers AuditReaderFactory с этим менеджером имен? Я думал, что, возможно, вызов метода в диспетчере сущностей создает временный сеанс, но это не происходит при использовании Envers, это правда?
Каков наилучший способ исправить эту проблему (чтобы AuditReaderFactory можно было использовать из каждого потока)?