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)?