Spring + Hibernate + Envers + multithreading - session closed

We use Hibernate (with JPA) and Hibernate Envers to save object history. A web application starts many threads, some of them are created by calling the RMI method from other applications, some of them are created by the application itself, and some of them are created to process HTTP requests (they generate views).

We also use the Open Session In View template for session management, 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> 

The database is accessed using the DAO, all of which have EntityManagers introduced by Spring.

 @PersistenceContext protected EntityManager em; @PersistenceUnit protected EntityManagerFactory emf; 

Everything worked well enough before we decided to use Hibernate Envers. When any thread that is not a thread that generates a view runs code to get the old version of the object, an 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 the Scheduler thread org.hibernate.SessionException: Session closed! in org.hibernate.internal.AbstractSessionImpl.errorIfClosed (AbstractSessionImpl.java:129) in org.hibernate.internal.SessionImpl.createQuery (SessionImpl.java:1776) in org.hibernate.envers.tools.query.QueryBuilder.uueryQueryBuilder .java: 226) in org.hibernate.envers.query.impl.AbstractAuditQuery.buildQuery (AbstractAuditQuery.java:92) in org.hibernate.envers.query.impl.EntitiesAtRevisionQuery.list. EntitiesAtRevisionQuery.ava. hibernate.envers.query.impl.AbstractAuditQuery.getSingleResult (AbstractAuditQuery.java:110) (...)

When the above code is run by the thread generating the view, it works fine. In addition, the non-envers code in the DAO works great 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 performed by RMI threads without problems.

Why can non-return flows call methods in an entity manager without exceptions, but cannot use Envers AuditReaderFactory with this entity manager? I thought that maybe calling a method in the entity manager creates a temporary session, but this does not happen when using Envers, is that true?

What is the best way to fix this problem (so that AuditReaderFactory can be used from each thread)?

+6
source share
1 answer

We did not find out why the call to EntityManagerFactory worked in non-ui thread methods, but the method calls to AuditReaderFactory not made. In any case, we have found a way to fix this.

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

It turned out that making a loadByRevision transaction loadByRevision not enough. If the object returned by this method contained persistent packages with lazy loading, access to them outside the scope of the loadByRevision methods raised a 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 loading (retrieving an object and accessing collections with a lazy loader) will be done inside a single method annotated using @Transactional .

+1
source

All Articles