2014-01-09 141 views
6

我們使用Hibernate(使用JPA)和Hibernate Envers來保存對象的歷史記錄。 Web應用程序運行很多線程,其中一些是通過其他應用程序的RMI方法調用創建的,其中一些是由應用程序本身創建的,其中一些創建來處理http請求(它們會生成視圖)。Spring + Hibernate + Envers +多線程 - 會話關閉

我們還使用打開會話視圖模式來管理會話,所以我們的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由Spring注入。

@PersistenceContext 
protected EntityManager em; 

@PersistenceUnit 
protected EntityManagerFactory emf; 

在我們決定使用Hibernate Envers之前,一切都運行良好。當任何不是視圖生成線程的線程運行代碼以獲取對象的舊版本時,將拋出異常。在線程 「計劃」 org.hibernate.SessionException

@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.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) (...)

當上面的代碼由視圖生成線程運行時,它工作正常。此外,DAO中的非envers代碼對每個線程均正常工作。例如,下面

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

代碼段可以通過RMI線程運行沒有問題。

爲什麼非視圖線程可以在沒有異常的情況下調用實體管理器上的方法,但不能將Envers的AuditReaderFactory與該實體管理器一起使用?我認爲可能調用實體管理器上的方法創建臨時會話,但在使用Envers時不會發生這種情況,這是真的嗎?

解決該問題的最佳方法是什麼(以便可以從每個線程使用AuditReaderFactory)?

回答

1

我們沒有找到爲什麼在非UI線程方法調用EntityManagerFactory工作,但方法調用AuditReaderFactory沒有。無論如何,我們找到了一種解決方法。

解決方案是用@Transactional註釋方法。如果在調用AuditReaderFactory之前調用鏈中的任何方法被標記爲@Transactional,則在非ui線程中不存在SessionException

事實證明,製作loadByRevision交易是不夠的。如果該方法返回的對象包含惰性加載的持久行李,則在loadByRevision方法範圍之外對其進行訪問會導致LazyInitializationException(沒有會話)。

最終的解決方案是確保如果任何線程想要從數據庫加載一些數據,則所有加載(獲取對象並訪問延遲加載的集合)將在用@Transactional註釋的一種方法內完成。

相關問題