kagamihogeの日記

kagamihogeの日記です。

Spring Framework Reference Documentation 4.1.xのV. Data Access 16. Transaction Management 16.6以降をテキトーに訳した

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ をテキトーに訳した。

16.6 Programmatic transaction management

Spring Frameworkではプログラム的なトランザクション管理には二つの方法を提供しています。

  • TransactionTemplateを使用する方法。
  • PlatformTransactionManager実装を直接使用する方法。

Springチームは、基本的には、プログラム的なトランザクション管理にはTransactionTemplateを推奨します。PlatformTransactionManagerを使う方法はJTAUserTransactionAPIに似ていますが、例外処理は少々マシです。

16.6.1 Using the TransactionTemplate

TransactionTemplateJdbcTemplateなどの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実装のビーンをただ単に渡すだけです。TransactionDefinitionTransactionStatusオブジェクトを使用することで、トランザクションの初期化・ロールバック・コミットが可能です。

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クラスはJTAUserTransactionTransactionManagerオブジェクトをルックアップするJNDIをオプションで実行可能です。アプリケーションサーバによって異なるTransactionManagerのロケーションの自動検出も可能です。JTATransactionManagerにアクセスすることで拡張トランザクションセマンティクス、特にトランザクションサスペンド、を利用可能です。詳細についてはJtaTransactionManagerを参照してください。

SpingのJtaTransactionManagerJava 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以降で利用可能な、IBMUOWManagerAPIを利用します。このアダプタにより、Spring駆動のトランザクションサスペンドPROPAGATION_REQUIRES_NEWで初期化したトランザクションサスペンドとレジューム)をIBMが公式にサポートしています。

16.9.2 Oracle WebLogic Server

WebLogic Server 9.0以上では、JtaTransactionManagerの代わりにWebLogicJtaTransactionManagerを通常は使用します。JtaTransactionManagerWebLogic専用サブクラスは、標準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トランザクションサポートに関する詳細な情報は以下の通りです。

*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の訳がイマイチわからない