- 6.1. トランザクション管理の中心となるインタフェース
- 6.2. トランザクション管理の準備
- 6.3. 宣言的トランザクション管理
6.3. 宣言的トランザクション管理
宣言的トランザクション管理を使用するとトランザクション管理をビジネスロジックから切り離すことができます。これは Spring AOP により実現されています。宣言的トランザクション管理によるとアプリケーションコード中にトランザクション制御コードを記述する必要がなくコードの見晴らしが良くなります。また、アプリケーションコードが Spring の API やその他のトランザクション API に依存することがありません。このため通常はプログラマティックなトランザクション管理よりも宣言的トランザクション管理を選択する方が良いでしょう。Spring でもトランザクション操作が少ない場合以外では宣言的トランザクション管理が推奨されています。
宣言的トランザクション管理を行うには、org.springframework.transaction.interceptor.TransactionProxyFactoryBean
クラスを使用します。Bean 定義ファイルでこのクラスのプロパティを設定するだけでコンテナがトランザクション管理を行ってくれます。設定すべきプロパティは以下の
3つです。
プロパティ名 | 型 | 設定内容 |
---|---|---|
target | java.lang.Object |
トランザクション管理を行いたいメソッドを持つクラス |
transactionManager | org.springframework.transaction.PlatformTransactionManager |
使用する PlatformTransactionManager インタフェースを実装する具象クラス |
transactionAttributes | java.util.Properties |
メソッド名をキーとし、トランザクションの振る舞い、分離レベル等トランザクション属性を値に持つ Properties |
このうちもっとも重要なのは transactionAttributes です。Properties のキー値となるメソッド名にはワイルドカード (*)
を使用することができます。キーに対応する値にはorg.springframework.transaction.TransactionDefinition
で定義されている項目の他、特定の例外が発生したときのロールバックルールを指定可能です。
設定項目 | 説明 |
---|---|
トランザクションの振る舞い | TransactionDefinition で定義されている定数フィールドを使用して指定 |
トランザクションの分離レベル | |
読み取り専用かどうか | 読み取り専用にする場合は readOnly を指定 |
タイムアウト時間 | 単位は "秒" |
発生したときの振る舞いを指定したい例外 | 発生したときの振る舞いを指定したい例外の完全限定名を、規定のプリフィクス ("+" or "-") をつけて指定 |
このうち必ず指定しなければならないのはトランザクションの振る舞いです。もし null 値や空文字を指定した場合は当該メソッドがトランザクション内で処理されません。指定順序は任意で、複数指定する場合は "," (カンマ) で区切って指定します。
Spring では宣言的トランザクション管理を行っているとき、非検査例外 (java.lang.RuntimeException およびそのサブクラス) が発生するとデフォルトで自動的にロールバックされます。逆に言えばそれ以外の例外 (検査例外) が発生してもコミットされます。場合によってはこのデフォルトの振る舞いを変えたいことがあるかもしれません。そのような場合には発生したときの振る舞いを指定したい例外の完全限定名にプリフィクスをつけて指定します。プリフィクスとして使用できるのは "+" と "-" で、指定した例外が投げられたときコミットする場合は "+" を、ロールバックするときは "-" を指定します。
<property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED,+MyUncheckedException</prop> <prop key="update*">PROPAGATION_REQUIRED,-MyCheckedException</prop> <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property>
名前が insert で始まるメソッドで非検査例外 MyUncheckedException が発生したとしてもトランザクションをコミットするよう設定し、名前が update で始まるメソッドで検査例外 MyCheckedException が発生したとしてもトランザクションをロールバックするようにしています。また、名前が前述の単語以外で始まるメソッドでは読み取り専用となるよう指定しています。
以下に具体的な使用例をあげます。最初に使用するデータベースのテーブル定義を示します。使用するのは以下のようなごく単純な定義を持つ person テーブルです。
カラム名 | データ型 | 備考 |
---|---|---|
id | integer | primary key |
name | varchar(16) |
次に使用するクラスです。上記テーブルのカラムに対応するプロパティを持つ Person クラスと以下のインタフェースを実装するクラスを使用することとします。
public interface PersonDao { public void insert(Person person); public List<Person> findAll(); public void update(Person person); public void setDataSource(DataSource dataSource); }
public interface PersonManager { public void insert(Person person); public void insertPersons(List<Person> persons); public void update(Person person); public void updatePersons(List<Person> persons); public List<Person> findAll(); public void setDao(PersonDao dao); }
最後に Bean 定義ファイルです。
<beans> ...(略)... <bean id="personManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target"> <bean class="[PersonManager の実装クラスの完全限定名]"> <property name="dao"> <bean class="[PersonDao の実装クラスの完全限定名]"> <property name="dataSource"><ref bean="dataSource"/></property> </bean> </property> </bean> </property> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> </beans>
target プロパティに PersonManager
の実装クラスを指定し TransactionProxyFactoryBean
でラッピングしています。使用するときには以下のようにします。
PersonManager manager = (PersonManager) context.getBean("personManager");
ラップしたクラスを ApplicationContext から取得しますが、org.springframework.transaction.interceptor.TransactionProxyFactoryBean
でキャストするのではなく PersonManager インタフェースでキャストすることに注意してください。
実習課題 1
6.3 で取り上げた例を完成させ、トランザクションの管理が行われていることを確認しなさい。
- PersonDao, PersonManager インタフェースを改良し、任意の例外が投げられるようにしても構わない
- 例では JDK 5.0 を前提にしているが JDK 1.4 での実装に変更しても構わない
- (ヒント) TransactionProxyFactoryBean の postInterceptors プロパティに
org.springframework.aop.interceptor.DebugInterceptor
を設定すると DEBUG レベルのログを出力することができる。