1.宣言的トランザクション
概要
Spring Transactionの中の宣言的トランザクションについて見ていきます。
宣言的トランザクションとは、「あるメソッドを呼び出したときにトランザクションをかける」と宣言する方法です。
宣言は、Springの設定ファイルに記述するだけです。
この方法は実質的に、トランザクションの記述を一切しなくてすむようになります。
宣言的トランザクションの基本的な考え方
宣言的トランザクションを使用した場合、beginTransaction、commit、rollbackは自動で行われます。
自動ということは何かそれぞれの処理が行われるきっかけがあるはずです。
ここではまず、それを説明しておこうと思います。
beginTransactionのきっかけ
宣言的トランザクションでは、トランザクションをかけるメソッドを指定するということを概要で書きました。
そうすると大体想像付くかと思いますが、指定したメソッドの開始がきっかけで、beginTransactionを実施します。
commit / rollbackのきっかけ
では、commit / rollbackのきっかけはというと、メソッドの終了です。
メソッドの終了までに、例外が発生しなければcommitし、例外が発生すればrollbackします。
ですので、1つの注意点としては、ロールバックさせたいときは例外を発生させなければならないということがあります。
しかし、これは無理な話ではなく、どちらかというと自然な仕様です。
また、rollbackする例外は自由に指定することができます。
メソッドの中でメソッドを呼ぶ場合
例えば、メソッドa、メソッドbの二つをトランザクションの対象にしたとします。
そして、メソッドaの中でメソッドbを呼び出していたとします。
【イメージ】
public void a(){
//
b();
}
このときは、a()メソッドを開始したときにbeginTransactionがされ、b()の開始時に再度、beginTransactionすることもできますし、
b()の開始と終了時にはトランザクション処理を行わないようにすることもできます。
宣言的トランザクションの仕組み
宣言的トランザクションは、メソッド名を指定するだけでトランザクション管理ができます。
プログラムを一切記述する必要がありません。
一体どのようにしてこんなことを実現しているのでしょうか?
実は、AOPを使用するのです。
AOPとは、バイナリのコードをロードするときにこっそりと他のコードを埋め込む技術です。
AOPを使用することで、ソースコードを一切変更せずに、例えばメソッドの実行前や実行後に他の処理を実行させることができます。
この機能を使用することで、トランザクションの処理を実現しているのです。
Daoの作り方
Springトランザクションでは、DaoをSpringの管理下に置く必要があります。
やり方はそれほど難しくありません。フレームワーク毎に管理下に置く方法が用意されています。
参考までにいくつかの種類についてサンプルを作りましたので、以下を参照ください。
参考:
81.Daoの作り方サンプル(通常の方法:SpringJDBC)
記述サンプル
それでは、実際に記述例を見ていきます。
ここでは特にDaoの実装は示しません。しかし、SpringJDBCやHibernate、IBatisなど有名なO/Rマッピングは簡単に使用できますのでご安心を。
【トランザクションの記述例】
<?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.xsdhttp://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.xsdhttp://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="testDao" class="dao.TestDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- ビジネスロジック -->
<bean id="testService" class="business.service.TestServiceImpl">
<property name="testDao" ref="testDao" />
</bean>
<!-- トランザクション管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 宣言的トランザクションの織り込み -->
<aop:config proxy-target-class="false">
<aop:advisor pointcut="execution(* business.service.*(..))" advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice" >
<tx:attributes>
<tx:method name="find*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
</beans>
ここでは、ビジネスロジックのメソッドにトランザクションをかけています。
findで始まるメソッドをread onlyにし、
それ以外は、例外が発生したときにロールバックするように記述しています。
以下では、上記の設定をしたときに具体的にどのような動きになるかを見ていきます。
【例1】
例えば、TestServiceImpl.insertMember() 、TestServiceImpl.updateMember() というメソッドがあったとします。
test()メソッド内で以下のように呼び出されたときにどのようにトランザクションが働くかを見ます。
<TestServiceサンプル>
package business.service;
class TestServiceImpl implements TestService {
private TestDao testDao;
public void insertMember(Member mem){ this.testDao.insMember(mem); }
public void updateMember(Member mem){ this.testDao.updMember(mem); }
public void test(){
insertMember();
updateMember();
}
}
まず、tx:adviceで、トランザクション制御の対象メソッド名を*で指定していますので、test()メソッドもトランザクション制御の対象になります。
そして、propagation="REQUIRED" を指定していますので、トランザクションが開始されていなければ開始され、開始されていれば開始しません。
この動作は以下のいくつかのケースで見ていくことにします。
※propagation属性の詳しい設定内容は、下の節で紹介していますので見てみてください。
①insertMember()を呼び出し、メソッド内で何か例外が発生した場合。
⇒insertMember()メソッド開始時にトランザクション開始します。
メソッドを抜ける時に、メソッド内での処理がすべてrollbackされます。もし例外が発生しなければinsertMember()メソッドを抜けるときにcommitされます。
②test()を呼び出し、insertMember()、updateMember() メソッドの両方とも例外が発生しなかった場合。
⇒test()メソッド開始時にトランザクション開始します。
test()メソッドを抜けるときに、両方ともcommitされます。
③test()を呼び出し、insertMember()は例外なし、updateMember()で例外が発生した場合。
⇒test()メソッド開始時にトランザクション開始します。
test()メソッドを抜けるときに、insertMember()の内容も、rollbackされます。
【例2】
トランザクション制御外のクラスでメソッドを呼んだ場合。
<サンプル>
package test;
class Test {
//DIでtestServiceのbeanを設定する
private TestService testService;
public void test2(){
testService.insertMember();
testService.updateMember();
}
}
まず、パッケージ名がtestなので、そもそもパッケージ自体がトランザクション制御外になります(aop:config タグを参照)ので、
test2()メソッドはトランザクション制御外となります。
propagation="REQUIRED" を指定していますので、例1と同様に以下のようになります。
①test2()メソッド内で、insertMember()、updateMember() メソッドの両方とも例外が発生しなかった場合。
⇒test2()メソッド開始時にはトランザクションは開始されません。
insertMember()メソッド開始時にトランザクション開始、メソッド終了時にcommitされます。
次にupdateMember()が呼ばれますが、メソッド開始時にトランザクション開始、メソッド終了時にcommitされます。
②test2()メソッド内で、insertMember()は例外なし、updateMember()で例外が発生した場合。
⇒insertMember()メソッド開始時にトランザクション開始、メソッドを終了時にcommitされます。
次にupdateMember()が呼ばれますが、メソッド開始時にトランザクション開始、メソッド終了時にrollbackされます。
Spring設定ファイルの簡単な説明
トランザクション管理に使用する名前空間は、txです。
そして、AOPでトランザクションを織り込みます。
上記の場合、business.serviceパッケージ配下のクラスを全て対象にしています。
execution(* business.service.*(..))
また、メソッドは以下のタグで宣言されています。
<tx:advice id="txAdvice" >
上記の場合、find*と記述されていますが、findで始まるメソッドについてはread onlyでトランザクションを制御することになります。
ですので、TestServiceImpl#findMember() のようなメソッドで、insertなどが実行されるとエラーになります。
エラーで例外が発生すれば、当然rollbackされます。
tx:methodタグのリファレンス
【tx:methodタグの属性】
ここで紹介した上記の機能は、宣言的トランザクション以外でも同じです。
どのような機能があるか、確認しておいていただければと思います。
Created Date: 2011/01/31