Seamの会話の開始、終了の指定方法とEntityManagerのflushモードについての注意点

Seamの場合、EntityManagerをSeam管理にして会話スコープと同期させることで、JPAの永続コンテキストをアプリケーショントランザクションの間持続させることが容易に可能です。たとえば、データ更新ウィザード画面を開始してから、最終画面の更新ボタンををクリックするまで永続コンテキストを持続させ、その間中ずっとエンティティをmanaged状態として保持させられます。この機能はSeamならではというか、さすがにHibernateのGavin King氏が設計しただけのことはあってJPASeamの相性が非常によいといわれる所以です。SpringやSeasar2などステートレスなアーキテクチャーではdetached状態のエンティティの扱いで注意が必要だったりDTOへの詰め替えが面倒だったりして結局JPAのよさが生かされない事が多いですので。
このように永続コンテキストをアプリケーショントランザクション中保持する場合の注意点として、通常アプリケーショントランザクションは複数のDBトランザクションから構成されるため、途中でEntityManagerのflush()が実行されてしまうとアプリケーショントランザクションとしての原子性が失われるという点を考慮しなくてはなりません。JPAのプロバイダーとしてHibernateを利用する場合はFlushModeをMANUALとして設定して、DBトランザクションのコミット時やクエリー発行時に自動的にflush()が実行されないようにすることが一般的です。*1
会話をbeginするタイミングでこのflushModeを指定できますが、毎回指定するのはいまいちなので以下のようにデフォルト値をcomponents.xmlで指定可能です。

<core:manager concurrent-request-timeout="500"
	conversation-id-parameter="cid" conversation-timeout="120000"
	parent-conversation-id-parameter="pid" 
	default-flush-mode="MANUAL"/>

それで、ここまでは本に書いてある通りなのですが、意外な点として注意することは以上のようにデフォルト値を指定した場合でも@Beginアノテーションを使って長期会話に入る場合は、上記のデフォルト設定が無視されるということです。@Beginアノテーションの定義を見ると、以下のようにflushMode属性のデフォルト値がFlushModeType.AUTOになっているため、結局flushModeでMANUALとすることを毎回明示的に指定しない限り、期待通りMANUALフラッシュモードにならないようです。

public @interface Begin 
{
	...
	
   /**
    * Set the FlushMode for any EntityManager used in
    * this conversation.
    */
   FlushModeType flushMode() default FlushModeType.AUTO;

}

結局components.xmlでの指定はpages.xmlで会話を開始するときのデフォルト値としてのみ有効なようです。この問題以外にもアノテーションで会話を開始、終了するのは

  • アクションメソッド呼び出し時にしか会話の境界が設定できない
  • outcomeなどによって会話を細かく制御できない(ifOutcome属性がDeprecatedなのはなぜ?)

などの問題もあるため、通常のベストプラクティスとしてはxmlファイルで宣言した方がよいことが多いと思います。

*1:この手法は標準化されておらず、他のJPAプロバイダーを使う場合は会話の途中の検索などの処理をautocomitモードでトランザクションサスペンドした状態で行うしかない。