10.6 Programmatic transaction management

プログラムでトランザクション制御する方法についてのおはなし。EJBで言うところのBMTですね。方法としてはTransactionTemplateを用いるやり方と、PlatformTransactionManagerを直接用いるやり方がある。Springの中の人的にはTransactionTemplateがオススメ。PlatformTransactionManagerを直接使うとJTAみたいな感じになります。

10.6.1 Using the TransactionTemplate

JdbcTemplateなどと同じようなコールバックによるアプローチ。コールバックにすることで、アプリケーションがリソース管理やコード値のハンドリングなどといった色々ウザイことを管理しなくてすみます。なおサンプルコードを見るとわかるが、TransactionTemplateを使うとSpringのトランザクションAPIとの結合度はこのアプローチを使っても上がるのは覚悟すること。

使い方は、TransactionCallbackインタフェースの実装を作成し、実装の中にトランザクショナルな処理を実装する。TransactionCallbackのインスタンスをTransactionTemplate#executeに渡す。


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);
}

public Object someServiceMethod() {
//TransacitonCallbackを生成し、トランザクショナルな処理コードを実装する。
return transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}

戻り値がいらない場合はTransactionCallbackのかわりに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();
}
}
});

10.6.1.1 Specifying transaction settings

トランザクションの属性(伝搬属性、分離レベルなど)はTransactionTemplate の属性として定義されており、デフォルト値は10.5.5 settingsと同じ。この値をプログラムもしくはIoCコンテナで設定する。

以下はプログラム上でAPIを直接使って設定する例。


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 seconds
// and so forth...
}
}

以下はIoCで設定する例。登録したインスタンスはインジェクションされるんでしょう。

<bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>"

最後にTransactionTemplateはスレッドセーフ。ただしトランザクションの設定状態は属性としてもつので、結論としてはトランザクションの設定ごとにインスタンスを定義するのが正義。

10.6.2 Using the PlatformTransactionManager

org.springframework.transaction.PlatformTransactionManager を使う方法。Beanの参照をIoCから直に捕まえて使うだけ。


DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
// execute your business logic here
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);