http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ をテキトーに訳した。
16.6 Programmatic transaction management
Spring Frameworkではプログラム的なトランザクション管理には二つの方法を提供しています。
TransactionTemplate
を使用する方法。PlatformTransactionManager
実装を直接使用する方法。
Springチームは、基本的には、プログラム的なトランザクション管理にはTransactionTemplate
を推奨します。PlatformTransactionManager
を使う方法はJTAのUserTransaction
APIに似ていますが、例外処理は少々マシです。
16.6.1 Using the TransactionTemplate
TransactionTemplate
はJdbcTemplate
などのSpringテンプレートと同様なアプローチを採用しています。
トランザクションリソースの獲得と解放というボイラープレートをアプリケーションコードを分離するために、コールバックの形態を取っています。その結果として、コードが意図を反映するものとなり、開発者が本質的に記述したいコードのみ書けるようになります。*1
以下の例で見ていくように、TransactionTemplate
を使うことはSpringのトランザクションインフラとAPIにアプリケーションのコードが強く結合します。プログラム的なトランザクション管理を使うかどうかに依らず、その技術が自分たちの開発に適しているかどうかの判断は、自分自身で下す必要があります。
トランザクションコンテキストでの実行が必須なアプリケーションコードは、以下に示すように明示的にTransactionTemplate
を使います。アプリケーション開発者はTransactionCallback
の実装(通常は無名内部クラスで表現)を行い、その実装にはトランザクションコンテキストで実行する必要のあるコードを含めます。それから、TransactionTemplate
の公開メソッドexecute(..)
にTransactionCallback
のインスタンスを渡します。
public class SimpleService implements Service { // このインスタンス内の全メソッドで共有する単一のTransactionTemplate private final TransactionTemplate transactionTemplate; // PlatformTransactionManagerを設定するのにコンストラクタインジェクション // を使用する public SimpleService(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "The ''transactionManager'' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { // 以下のメソッドのコードはトランザクションコンテキストで実行される public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } }); } }
戻り値が無い場合、以下のように無名クラスと共にTransactionCallbackWithoutResult
を使います。
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
コールバック内のコードは引数として渡されるTransactionStatus
オブジェクトのsetRollbackOnly()
メソッドを呼ぶことでトランザクションをロールバック出来ます。
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { try { updateOperation1(); updateOperation2(); } catch (SomeBusinessExeption ex) { status.setRollbackOnly(); } } });
Specifying transaction settings
伝播モード・分離レベル・タイムアウトなどのトランザクション設定を、TransactionTemplate
にプログラム的かコンフィグレーションかのどちらかを使用して、指定可能です。デフォルトではTransactionTemplate
インスタンスはデフォルトトランザクション設定になっています。以下の例はTransactionTemplate
にトランザクション設定をプログラム的にカスタマイズする方法について示しています。
public class SimpleService implements Service { private final TransactionTemplate transactionTemplate; public SimpleService(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "The ''transactionManager'' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); // 必要に応じて明示的にトランザクション設定を設定する this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); this.transactionTemplate.setTimeout(30); // 30 秒 // などなど... } }
以下の例はSpring XMLコンフィグレーションを使用してカスタムのトランザクション設定をTransactionTemplate
に定義しています。sharedTransactionTemplate
は必要に応じて複数のサービスにインジェクト可能です。
<bean id="sharedTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/> <property name="timeout" value="30"/> </bean>
最後に、TransactionTemplate
クラスのインスタンスはスレッドセーフであり、よってこのインスタンスは会話的状態(conversational state)を維持しません。TransactionTemplate
インスタンスは設定状態を保持し続けるので、複数クラスで単一のTransactionTemplate
インスタンスを共有可能です。もし異なる状態のTransactionTemplate
(例えば異なる分離レベル)がクラスで必要な場合、二つの独立したTransactionTemplate
インスタンスを作成する必要があります。
16.6.2 Using the PlatformTransactionManager
トランザクション管理にorg.springframework.transaction.PlatformTransactionManager
を直接使うことも可能です。ビーン参照経由でPlatformTransactionManager
実装のビーンをただ単に渡すだけです。TransactionDefinition
とTransactionStatus
オブジェクトを使用することで、トランザクションの初期化・ロールバック・コミットが可能です。
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); // プログラム的にだけ指定可能なトランザクション名を明示的に設定 def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { // ここでビジネスロジックを実行 } catch (MyException ex) { txManager.rollback(status); throw ex; } txManager.commit(status);
16.7 Choosing between programmatic and declarative transaction management
トランザクション関連の操作がごく少数であるなら、プログラム的なトランザクションは基本的には良いアイデアです。たとえば、あるwebアプリケーションにおいて特定の更新操作のみでしかトランザクションを必要としないのであれば、Springや他のテクノロジを用いてトランザクショナルなプロキシをセットアップしなくても良いでしょう。この場合、TransactionTemplate
は恐らく良いアプローチとなるでしょう。また、明示的にトランザクション名を設定可能なのはトランザクション管理にプログラム的アプローチを使用する場合だけです。
他方で、アプリケーションに多数のトランザクション操作がある場合、宣言的管理に価値を見出せます。ビジネスロジックの外にトランザクション管理を追い出す設定はそう難しいことではありません。Spring Frameworkを使う場合、EJB CMTと比べると、宣言的トランザクション管理の設定コストはかなり減少します。
16.8 Transaction bound event
Spring 4.2以降、イベントリスナーをトランザクションのフェーズにバインド可能になりました。良くある例としてはトランザクション正常完了時のイベント処理です。カレントトランザクションの結果がリスナーにとって重要である場合、イベントを柔軟に使うことが可能です。
イベントリスナーの登録は@EventListener
アノテーションで行います。そのイベントリスナーをトランザクションにバインドするには@TransactionalEventListener
を使います。この場合、リスナーはデフォルトではトランザクションのコミットフェーズにバインドされます。
この概念を説明するためのサンプルを見てみます。いま、注文生成イベントをパブリッシュするコンポーネントがあるとして、トランザクション正常コミット時点でパブリッシュされるイベントのみ処理するリスナを定義したい、とします。
@Component public class MyComponent { @TransactionalEventListener public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) { ... } }
TransactionalEventListener
アノテーションが公開するphase
属性を用いてリスナをバインドしたいトランザクションのフェーズをカスタマイズ可能です。妥当なフェーズはBEFORE_COMMIT
, AFTER_COMMIT
(デフォルト), AFTER_ROLLBACK
, AFTER_COMPLETION
で、AFTER_COMPLETION
はトランザクション(のコミットかロールバックによる)完了を集約します。
トランザクションが実行されない場合、必要なセマンティクスが無いので、リスナーは全く呼び出されません*2。アノテーションのfallbackExecution
属性をtrue
に設定することで振る舞いをオーバーライド可能です。
16.9 Application server-specific integration
Springのトランザクション抽象化は基本的にはアプリケーションサーバに依存しません(application server agnostic)。加えて、SpringのJtaTransactionManager
クラスはJTAのUserTransaction
とTransactionManager
オブジェクトをルックアップするJNDIをオプションで実行可能です。アプリケーションサーバによって異なるTransactionManager
のロケーションの自動検出も可能です。JTAのTransactionManager
にアクセスすることで拡張トランザクションセマンティクス、特にトランザクションのサスペンド、を利用可能です。詳細についてはJtaTransactionManager
を参照してください。
SpingのJtaTransactionManager
はJava EEアプリケーションサーバ上で動作させる場合には標準的な選択肢で、一般的なすべてのサーバで動作します。トランザクションのサスペンドなど拡張機能についても同様に、特別な設定を必要とせず、GlassFish, JBoss Geronimoなどの多数のサーバで動作します。ただし、完全なトランザクションサスペンドサポートとその他の拡張インテグレーション(fully supported transaction suspension and further advanced integration)については、SpringはWebLogic ServerとWebSphere用の専用アダプターを用意しています。これらのアダプターは以降のセクションで説明します。
WebLogic ServerとWebSphereを含む標準的なシナリオでは、<tx:jta-transaction-manager/>
設定要素を使うことになると思われます。この場合、この要素は基底サーバを自動検出し、プラットフォームで利用可能な最良のトランザクションマネージャを選択します。つまり、明示的にサーバ固有のアダプタクラス(以降のセクションで解説)を設定する必要は無く、自動的に選択されます。デフォルトのフォールバックは標準JtaTransactionManager
です。
16.9.1 IBM WebSphere
WebSphere 6.1.0.9以上で推奨されるSpring JTAトランザクションマネージャはWebSphereUowTransactionManager
です。この専用アダプタは、WebSphere Application Server 6.0.2.19以降と6.1.0.9以降で利用可能な、IBMのUOWManager
APIを利用します。このアダプタにより、Spring駆動のトランザクションサスペンド(PROPAGATION_REQUIRES_NEW
で初期化したトランザクションのサスペンドとレジューム)をIBMが公式にサポートしています。
16.9.2 Oracle WebLogic Server
WebLogic Server 9.0以上では、JtaTransactionManager
の代わりにWebLogicJtaTransactionManager
を通常は使用します。JtaTransactionManager
のWebLogic専用サブクラスは、標準JTAのセマンティクスだけでなく、WebLogicマネージドトランザクション環境のSpringのトランザクション定義を完全にサポートします。この機能には、トランザクション名・トランザクションごとの分離レベル・あらゆるケースにおけるトランザクションの適切なレジューム、が含まれます。
16.10 Solutions to common problems
16.10.1 Use of the wrong transaction manager for a specific DataSource
使用するトランザクション環境と要求に基づき、正しい(correct)PlatformTransactionManager
実装を使用してください。適切な使用により、Spring Frameworkはシンプルでポータブルな抽象化を提供します。グローバルトランザクションを使う場合、すべてのトランザクション操作においてorg.springframework.transaction.jta.JtaTransactionManager
(もしくはアプリケーションサーバ固有のサブクラス)の使用が必須です。そうしない場合、トランザクションインフラはコンテナのDataSource
インスタンスなどのリソース上でローカルトランザクションの実行を試みます。このようなローカルトランザクションは意味をなさず、お行儀の良いアプリケーションサーバはそのような操作をエラーとして扱います。
16.11 Further Resources
Spring Frameworkのトランザクションサポートに関する詳細な情報は以下の通りです。
- Distributed transactions in Spring, with and without XAはSpringのDavid Syer氏がJavaWorldのプレゼンテーションで用いたもので、Springアプリケーションの分散トランザクションの7つのパターンを解説し、三つがXAで四つはXA以外です。
- Java Transaction Design StrategiesはInfoQで利用可能な書籍で、Javaのトランザクションに関するほどよい入門書です。また、Spring FrameworkとEJB3両方のトランザクションの使用と設定方法のサンプルについても掲載されています。
*1:and results in code that is intention driven, in that the code that is written focuses solely on what the developer wants to do.が原文。intention drivenとか日本語当てるのが難しすぎたのでテキトーに意訳した。ここで言いたいことは『お定まりのコードが無くなるのでビジネスロジックに集中できる』くらいの意味と思うので。
*2:the listener is not invoked at all since we can’t honor the required semanticsが原文。トランザクションが無いならリスナーが実行されない、という当たり前のことを書いているのだと思う。we can’t honorの訳がイマイチわからない