2014-01-09 5 views
6

Hibernate(JPA付き)とHibernate Enversを使用してオブジェクトの履歴を保持します。 Webアプリケーションは多くのスレッドを実行し、それらの一部は他のアプリケーションからのRMIメソッド呼び出しによって作成され、その一部はアプリケーション自身によって作成され、一部はHTTP要求を処理するために作成されます(ビューを生成します)。私たちのweb.xmlが含まれているので、Spring + Hibernate + Envers +マルチスレッド - セッションが閉じられる

我々はまた、セッションを管理するための表示パターンでオープンセッションを使用します。

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

データベースは、それらのすべては春によって注入EntityManagersを持っている、のDAOを使用してアクセスされます。

@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ました。 org.hibernate.envers.query.impl.EntitiesAtRevisionQuery.listでQueryBuilder.toQuery(QueryBuilder.java:226) でorg.hibernate.envers.query.impl.AbstractAuditQuery.buildQuery(AbstractAuditQuery.java:92) ( EntitiesAtRevisionQuery.java:108) でorg.hibernate.envers.query.impl.AbstractAuditQuery.getSingleResult(AbstractAuditQuery.java:110) (...)

上記のコードをビュー生成スレッドで実行するとうまくいきます。また、DAOのnon-enversコードはすべてのスレッドで正常に動作します。例えば、

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

以下のスニペットは問題なくRMIスレッドで実行することができます。

エンティティマネージャのメソッドを例外なしで呼び出すことはできますが、そのエンティティマネージャでEnversのAuditReaderFactoryを使用することはできません。おそらくエンティティマネージャのメソッドを呼び出すと一時的なセッションが作成されると思っていましたが、Enversを使用してもそれは起こりません。

この問題を解決する最善の方法は何ですか(AuditReaderFactoryをすべてのスレッドから使​​用できるようにするには)?

答えて

1

EntityManagerFactoryの非UIスレッドのメソッド呼び出しでは機能しませんでしたが、AuditReaderFactoryのメソッド呼び出しでは機能しませんでした。とにかく、それを修正する方法を見つけました。

解決策は、@Transactionalでメソッドに注釈を付けることでした。 AuditReaderFactoryの呼び出し前にコールチェーン内のメソッドが@Transactionalとマークされていた場合、非UIスレッドにはSessionExceptionがありませんでした。

loadByRevisionをトランザクションにすることでは不十分であることが判明しました。そのメソッドから返されたオブジェクトに遅延ロードされた永続バッグが含まれていた場合、loadByRevisionメソッドのスコープ外にあるアクセスはLazyInitializationException(セッションが存在しません)でした。

最終的な解決策は、いずれかのスレッドがデータベースからいくつかのデータをロードしたい場合は、すべてのロード(オブジェクトを取得し、レイジーロードされたコレクションにアクセスするには)@Transactionalでアノテート1つのメソッドの内部で行われることを確認しました。

関連する問題