Spring + Hibernate + Envers + multithreading - session is closed

hibernate, hibernate-envers, java, jpa, spring

Solution

We didn't find out why in non-ui threads method calls on `EntityManagerFactory` worked but method calls on `AuditReaderFactory` didn't. Anyway, we found a way to fix it.

The solution was to annotate methods with `@Transactional`. If any method in the call chain before call to the AuditReaderFactory was marked as `@Transactional`, there was no `SessionException` in non-ui threads.

It turned out that making `loadByRevision` transactional was not sufficient. If an object returned by that method contained lazy-loaded persistent bags, access to them outside the `loadByRevision` method scope caused `LazyInitializationException` (there was no session).

The final solution was to make sure that if any thread wants to load some data from the database, all the loading (getting an object and accessing lazy-loaded collections) will be done inside one method annotated with `@Transactional`.

Problem

We use Hibernate (with JPA) and Hibernate Envers to persist history of objects. The web application runs many threads, some of them are created by RMI method invocation from other applications, some of them are created by the application itself and some of them are created to handle http requests (they generate views). We also use the Open Session In View pattern to manage sessions, so our web.xml contains: ``` <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> ``` Database is accessed using DAOs, all of them have EntityManagers injected by Spring. ``` @PersistenceContext protected EntityManager em; @PersistenceUnit protected EntityManagerFactory emf; ``` Everything worked quite well before we decided to use Hibernate Envers. When any thread that is not a view-generating thread runs the code to get an old version of an object, the exception is thrown. ``` @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; } ``` Exception in thread "Scheduler" org.hibernate.SessionException: Session is closed! 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.java:226) at org.hibernate.envers.query.impl.AbstractAuditQuery.buildQuery(AbstractAuditQuery.java:92) at org.hibernate.envers.query.impl.EntitiesAtRevisionQuery.list(EntitiesAtRevisionQuery.java:108) at org.hibernate.envers.query.impl.AbstractAuditQuery.getSingleResult(AbstractAuditQuery.java:110) (...) When the code above is run by the view-generating thread it works fine. Also, the non-envers code in DAO works fine for every thread. For example, the snippet below ``` @Override public O load(Long id) { final O find = em.find(getBaseClass(), id); return find; } ``` can be run by RMI threads without problems. Why can non-view threads call methods on the entity manager without exceptions but cannot use Envers' AuditReaderFactory with that entity manager? I thought that maybe calling a method on the entity manager creates a temporary session but that doesn't happen when using Envers, is that true? What is the best way to fix that issue (so that the AuditReaderFactory can be used from every thread)?

Original source