2013-04-17 10 views
17

私は、AspectJの読み込み時にSpring CGlibプロキシを使用するように作業しているアプリケーションを切り替えました。そして、私がやった直後に、私が休止状態になるのを開始したコードの多くの部分がありました。例外がスローされます。なぜ@Transactional(propagation = Propagation.SUPPORTS、readOnly = true)はHibernate Lazy Loading例外を修正しますか?

これらの遅延読み込み例外を解決するには、トランザクショナル属性を持たず、データベースからデータを読み込むためのスプリングリポジトリと呼ばれる以前のパブリックメソッドのほうに@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)を追加することができました。

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)を追加することで、休止状態の遅延読み込み例外がなくなり、なぜこれらの注釈がAspectJの読み込み時間織りでは必須ではなかったのですか?

アップデートは2 私は、AspectJのを削除すると問題ませんでしたが、問題は、私は本当にSUPPORTS伝播の実際の動作を理解していなかったということであったと信じています。特にSUPPORTSがJPA EntityManagerとどのようにやりとりしたのかを知るために、遅延ロード例外を引き起こすSUPPORTS伝播を一括して取り除きました。 Spring Transaction Managerのソースコードを読み終えたら、何をすべきかが明確になりました。 Springのドキュメンテーションが本当によく指摘していない重要なアイデアは、@ TransactionalアノテーションがEntityManagerのライフサイクルをトランザクションメソッドの開始と終了に結びつける同期ポイントとして使用されていることです。また、非常にアップデート1

http://www.ibm.com/developerworks/java/library/j-ts1/での記事のこのシリーズをお勧めしますと、このブログの記事http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/

これは、AOPプロキシを経由しないプライベート@Transactionalメソッドの呼び出しの場合ではありません。これらの問題は、他のサービスから呼び出されているパブリックメソッドで発生しています。

コード構造の例を示します。ここで問題が発生しています。

@Service 
public class FooService 
{ 
    @Autowired 
    private BarService barService; 

    public void someMethodThatOnlyReads() { 
     SomeResult result = this.barService.anotherMethodThatOnlyReads() 

     // the following line blows up with a HibernateLazyLoadingEcxeption 
    // unless there is a @Transactional supports annotation on this method 
     result.getEntity().followSomeRelationship(); 
    } 

} 

@Service 
public class BarService 
{ 
    @Autowired 
    private BarRepository barRepo; 

    public SomeResult anotherMethodThatOnlyReads() 
    { 
     SomeEntity entity = this.barRepo.findSomeEntity(1123); 
     SomeResult result = new SomeResult(); 
     result.setEntity(entity); 
     return result; 
    } 
} 

@Repository 
public class BarRepository 
{ 
    @PersistenceContext 
    private EntityManager em; 

    public SomeEntity findSomeEntity(id Integer) 
    { 
     em.find(SomeEntity.class,id); 
    } 
} 

答えて

9

あなたのコードはOpenSessionInViewFilterまたはこれに類するものを使用していないものとします。

@Transactional注釈がない場合、HibernateセッションはBarRepository.findSomeEntity()メソッドの終了後に閉じられます。 @Transactionalメソッドが呼び出されるとTransactionalInterceptorが適切(CGLIBプロキシまたは任意の他のAOPの設定を通してあなたはSpringコンテキストを持っている)メソッドにバインドされ、その後、セッションは全体のために春によって開放状態に保持される

これにより、遅延読み込み例外を防ぐことができます。あなたは、コード・フロー・春にどの点で正確org.springframework.transactionorg.springframework.orm.hibernate3DEBUGにロガー、特にHibernateTransactionManagerクラスとorg.springframework.transaction.support.AbstractPlatformTransactionManager(またはhibernate4あなたは4が休止状態にある場合)、あなたが表示されるはずのログを上げる決定された場合

休止状態のセッションを開いたり閉じたりする必要があります。ログには、セッションまたはトランザクションが各ポイントでオープン/クローズされる理由も示されます。

+0

デバッグを有効にしていくつかのテストプログラムを書いて、春のソースコードを読んでいますが、私はバネtxがSUPPORTS伝播の動作を誤解しています。 – ams

+0

驚いたことに、あなたは何を学んだのですか?SUPPORTSの実際の行動は何でしたか?それはあなたの最初の印象とどう違っていましたか? –

+2

私はこの質問への回答としてかなり良い要約を書いています。http://stackoverflow.com/a/16557225/438319 – ams

8

なぜそれが起こるのか完全にわかりませんが、私の理論は以下のとおりです。

AspectJ製織からCGLIBプロキシに移動すると、同じオブジェクトから呼び出されたメソッドに配置された@Transactional注釈が有効になりません。つまり、これらのメソッド内のコードは非トランザクション的に実行されます(@Transacionalが実際に有効になるコールスタックに別の@Transacionalメソッドがない限り)。

Javadoc for Propagation.SUPPORTSは言う:

注:それは同期がために適用されるトランザクション・スコープを定義すると、トランザクションの同期でトランザクション・マネージャーの、PROPAGATION_SUPPORTSは、まったくの取引とは若干異なります。 結果として、同じリソース(JDBC接続、Hibernateセッションなど)が指定されたスコープ全体で共有されます。これは、トランザクションマネージャの実際の同期設定によって異なります。

コードが非トランザクションで実行されるとき、オブジェクトをロードするために使用されるHibernate Sessionは、遅延プロパティのその後の初期化では使用できません。コードスタックのトップレベルメソッドに@Transactional(propagation = Propagation.SUPPORTS)と注釈を付けると、そのメソッドを終了するまでHibernate Sessionが利用可能になります。

関連する問題