前の記事 で見たプログラムによるトランザクションを見ていきます。
実はSpringが提供するプログラムによるトランザクション管理は、2種類あります。
まずはその一つ目です。
例によって、サンプルを中心に見ていきます。
【ビジネスロジック】public interface MemberService{ List<String> find(String name); } //setter/getterは記述を省略しています public class MemberServiceImpl implements MemberService{
private MemberDao memberDao; private TransactionTemplate transactionTemplateRequired;
void doSomething(String name){
//トランザクションを管理する (返り値が無い場合) transactionTemplateRequired.execute(new TransactionCallbackWithoutResult() {
//このメソッド内で実行したSQLがトランザクション管理されます。 protected void doInTransactionWithoutResult(TransactionStatus status) { memberDao.insert(); memberDao.update(); }
}); }
}
【Spring設定ファイル(AppricationContext.xml)】
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans
"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop ="http://www.springframework.org/schema/aop
"
xmlns:tx ="http://www.springframework.org/schema/tx
"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
">
<!-- Data Source (例えばPostgres)--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.postgresql.Driver"/> <property name="url" value="jdbc:postgresql://localhost/XXXX" /> <property name="username" value="XXX" /> <property name="password" value="XXX" /> </bean>
<!-- DAO --> <bean id="memberDao" class="dao.MemberDaoImpl"> <property name="dataSource" ref="dataSource" /> </bean> <!-- ビジネス --> <bean id="memberService" class="business.service.MemberServiceImpl"> <property name="transactionTemplateRequired" ref="transactionTemplateRequired" /> </bean>
<!-- トランザクション管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<!-- トランザクション処理用クラス。 --> <bean id="transactionTemplateRequired" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager" /> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" /> </bean>
</beans>
【説明】
<やること>
MemberServiceImplクラス内の、doSomethig()メソッドを見て下さい。
再掲:
//トランザクションを管理する (返り値が無い場合)
transactionTemplateRequired.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
//このメソッド内で実行したSQLがトランザクション管理されます。
memberDao.insert();
memberDao.update();
}
});
setterでspringの設定ファイルから設定したtransactionTemplateRequiredを使用しています。
executeメソッドを呼び出していますが、引数はコールバッククラスTransactionCallbackWithoutResultです。
このクラスは無名クラスにして、メソッドdoInTransactionWithoutResultを実装しています。
結局、このメソッド内にトランザクション処理を書けばいいだけです。
<トランザクション属性の設定>
さて、宣言的トランザクションなどの他のトランザクション処理では、いつトランザクションを開始するかを指定できました。
このサンプルではどのように指定しているでしょうか?
Springの設定ファイルのtransactionTemplateRequiredを見てください。
再掲:
<!-- トランザクション処理用クラス。 -->
<bean id="transactionTemplateRequired"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" />
</bean>
propagationBehaviorNameで設定しているのが分かるでしょうか?
PROPAGATION_REQUIREDになっているので、トランザクションを開始していなければ開始します。
この設定値の詳細は、以下のリンクの一覧表を参照してください。
・トランザクションのコードを書かないようにするには?(宣言的トランザクション)
先頭に、PROPAGATION_が付きますが同じになっているのが分かると思います。
これだけで終わりです!
ここでは、1つのクラスMemberServiceImplだけにtransactionTemplateRequiredを設定していますが
使いまわして、他のクラスに設定してもOKです。
ただし、注意点があります。
【TransactionTemplateの使用上の注意点】
TransactionTemplateは、setterでいくつかのプロパティ値を設定ができます。
しかし、プロパティ値はスレッドセーフではないので、途中で値を変えられると他のスレッドにも影響を与えます。
その代わり、executeメソッドはスレッドセーフです。
ですので、setterでプロパティ値さえ変更しなければ、使いまわしOKなのです
やっぱり、クラスごとに設定したりするのは大変なので、使いまわしできるのは便利ですね。
【TransactionTemplateによるトランザクションの使いどころ】
使いどころは、メソッド内で複数のトランザクションを処理しないといけない場合だと思います。
もし、1つメソッドに対して1つのトランザクションをかけるなら、アノテーションによるトランザクション、宣言的トランザクションでいいかと思います。
例えば以下のようなトランザクション処理を、宣言的、もしくはアノテーションによるトランザクション処理で実現しようとすると、かなりやりづらいはずです。
例:サンプルイメージです
public void doSomething(){
try{
//1つめのビジネスロジックの呼び出し
transaction.begin();
memberService.insert(true);
}catch(Exception e){
transaction.rollback();
}
try{
//2つめのビジネスロジックの呼び出し
transaction.begin();
memberService.insert(false);
transaction.commit();
}catch(SQLException e){
transaction.rollback();
}catch(Exception e){//SQLエラー以外はコミットする
transaction.commit();
}
}
メソッド内で2つが別々のトランザクションになっているので、
doSomethingメソッドに宣言的トランザクションをかけられません。
また、memberService.insert()メソッドにかけることもできません。
同じメソッドの呼び出しが2回行われていますが、それぞれトランザクション処理のやり方が違うからです(catchのところ)。
こんなときはプログラムによる処理をするほうが楽です。
また、TransactionTemplateは使い回しができるので、いくつも同じトランザクション処理をする箇所に使用するといいと思います。
とはいってもそれ程トランザクション処理のパターンは無いかと思います。
ですので、以下のようなものを用意して、プログラム側で適当にインジェクションしていくのがいいかと思います。
・PROPAGATION_REQUIREDを設定したTransactionTemplateのbean
・PROPAGATION_TESTEDを設定したTransactionTemplateのbean
・例外クラス毎にrollback/commitを設定したTransactionTemplateのbean
などなど
【補足】
サンプルでは、トランザクション処理の結果、返り値が必要ないパターンを書きました。
もし返り値が必要な場合は、以下のようにコールバックするクラスを変更します。
例:
//トランザクションを管理する (返り値がある場合)
Object ret = transactionTemplateRequired.execute(new TransactionCallback() {
//このメソッド内で実行したSQLがトランザクション管理されます。
protected void doInTransaction(TransactionStatus status) {
Object ret = memberDao.insert();
memberDao.update();
return ret;
}
});
簡単ですよね!
参照:
・トップ
Spring本家ドキュメントでのトランザクションの説明