Spring Framework 入門記 Transactionその3 JTA
今日は待ち時間の長い作業をやっているため普段より早めの入門記,今回はJTA(Java Transaction API)を使用します.
JTAといえば,先日書いた「JTA/JTS」なんですが,はぶさんの日記からたどって来てくれた人がたくさんいらしゃるようで.リファ見てる限り過去最高です.それまでは「僕の生きる道」のキーワードからが最高だったのですが,さすがはぶさん,人気ドラマを越えてます.すごいなぁ.
その「JTA/JTS」で書いたように,JTAはX/Open DTPの焼き直しのような仕様で,
UserTransaction
- アプリとトランザクションマネージャ間のインタフェース.X/Open DTPのTXに相当.
XAResource
- トランザクションマネージャとリソースマネージャ間のインタフェース.X/Open DTPのXAに相当.
という二つの主要なinterface
があるのですが,JTAにはもう一つ,
TransactionManager
- アプリケーションサーバ(コンテナ)とトランザクションマネージャ間のインタフェース.
というのも用意されています((JTAを実装するには他のinterface
も理解する必要がありますが,ここでは不要ということで他は無視.)).
X/Open DTPの頃は,TMは事実上TPモニタそのもので,独立したコンポーネントという扱いではなかったのですが,JTAでは一つのコンポーネントとしてプラガブルに扱えるようにデザインされているのですね.
これらのinterface
のうち,XAResource
は通常JDBCドライバの一部として提供されたりします.そして,UserTransaction
やTransactionManager
は,JTAの実装が提供します.
Springでは独自のJTA実装を提供していませんが,別途JTAを用意して利用することができるようになっています.
そのためには,前回まで使ってきたDataSourceTransactionManager
に代えて,JtaTransactionManager
を使用します.これも当然,PlatformTransactionManager
をimplements
したクラスです.
JtaTransactionManager
は,次のプロパティを持っています.
userTransaction
userTransactionName
transactionManager
transactionManagerName
jndiTemplate
このうち,userTransactionName
とtransactionManagerName
は,JNDIからルックアップしてそれぞれのインスタンスを取得する際に設定します.その場合は,InitialContext
の初期化に必要なプロパティ(Properties
の意味のプロパティ)をjndiTemplate
に設定します.
JNDIを使わない場合は,userTransaction
およびtransactionManager
を設定します.
なお,userTransaction
をimplements
したクラスがTransactionManager
もimplements
している場合は,transactionManager
またはTransactionManagerName
の設定は省略できます.これ,逆でもいいと思うんですけどねぇ.インタフェース的にUserTransaction
はTransactionManager
のサブセットなので,TransactionManager
が設定されていればUserTransaction
は設定しなくてもいいっていう方がいいような気がするのですが... 無念だ.
SpringでJTAを使うのに必要なことは以上でOKみたいです.
それで次はJTA実装を用意するわけですが,通常はアプリケーションサーバが提供するJTA実装を使うことが多いのだろうと思われます.しかし今回は,我らがひがさんによるJTA実装,S2JTAをSpringで使ってみることにしましょう.S2JTAはS2のコンテナとは独立しているということで,たぶんSpring上でも使えるとひがさんの03/25の日記に書かれていたので,いつかやらねばと思っていたのです.ついに,その日がやってまいりました!
ということで,S2JTAを見ていきます.
S2JTAはS2コンテナからは独立しているのですが,そのS2JTAをS2コンテナに組み込むための設定ファイルがj2ee-config.xml
です.その解説が「コネクションプーリング」というドキュメントにまとまっています((ConnectionPoolImpl
のxaDataSource
というプロパティはXADataSource
が正しいと思われ>ひがさん)).なるほど.これをそのままSpringの定義ファイルに持っていけばよさげです.
必要なコンポーネントは次の4つです.
org.seasar.extension.jta.TransactionManagerImpl
TransactionManager
をimplements
したクラス.文字通りのトランザクションマネージャです.org.seasar.extension.dbcp.impl.XADataSourceImpl
XAResource
をimplements
したクラスで,普通(XA非対応)のJDBCドライバをJTAで使えるようにしてくれます.org.seasar.extension.dbcp.impl.ConnectionPoolImpl
- いわゆるコネクションプールで,トランザクションマネージャと連携して,トランザクション終了時にコネクションをプールに戻してくれたりします.
org.seasar.extension.dbcp.impl.DataSourceImpl
DataSource
をimplements
したクラス.コネクションプールをDataSource
として使うためのアダプタという位置付けみたいです.
このように,トランザクションマネージャと連携するコネクションプール上にDataSource
を用意してくれているので,SpringのDataSourceUtils
のようなものが不要なんですね.さすが.
ちょっと問題になったのは,(2.0.5以前の)S2にはUserTransaction
をimplements
したクラスが存在しないこと.S2で使う分には必要ありませんからねぇ.ということで,S2のTransactionManagerImpl
を次のように修正しました.
public final class TransactionManagerImpl implements TransactionManager, UserTransaction {
先ほども書いたように,UserTransaction
はTransactionManager
のサブセットなので,新たにメソッドを実装する必要はありません.この修正はS2の新しいリリースに含めてもらえるみたい? です.
さて,それではSpringでS2のJTAを使うための定義ファイルです.基本的に前々回のTransactionProxyFactory
版をベースに,JTA対応を行いました.こんな感じです.
<!-- S2の設定 --> <bean id="jtaTM" class="org.seasar.extension.jta.TransactionManagerImpl"/> <bean id="xaDataSource" class="org.seasar.extension.dbcp.impl.XADataSourceImpl"> <property name="driverClassName"><value>org.hsqldb.jdbcDriver</value></property> <property name="URL"><value>jdbc:hsqldb:.</value></property> <property name="user"><value>sa</value></property> <property name="password"><value></value></property> </bean> <bean id="connectionPool" destroy-method="close" class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl"> <property name="transactionManager"><ref bean="jtaTM"/></property> <property name="XADataSource"><ref bean="xaDataSource"/></property> </bean> <bean id="dataSource" class="org.seasar.extension.dbcp.impl.DataSourceImpl"> <constructor-arg><ref bean="connectionPool"/></constructor-arg> </bean> <!-- Spring TMの設定 --> <bean id="springTM" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="userTransaction"><ref bean="jtaTM"/></property> </bean> <bean id="debug" class="org.springframework.aop.interceptor.DebugInterceptor"/> <!-- アプリケーションの設定 --> <bean id="foo" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target"> <bean class="study.Foo"> <property name="dataSource"><ref bean="dataSource"/></property> </bean> </property> <property name="transactionManager"><ref bean="springTM"/></property> <property name="transactionAttributes"> <props> <prop key="insert">PROPAGATION_REQUIRED,+java.lang.UnsupportedOperationException</prop> <prop key="update">PROPAGATION_REQUIRED,-java.io.IOException</prop> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> <property name="postInterceptors"><ref bean="debug"/></property> </bean>
うん,やっぱりTransactionProxyFactory
の方が素敵!(キラッ)
S2JTAの設定のところは,ほとんどj2ee-config.xml
のパクリです.
そして,SpringのTMとしてJtaTransactionManager
を定義しています.そのuserTransaction
プロパティには,S2JTAのTransactionManagerImpl
を設定しています.これはTransactionManager
でもありますから,transactionManager
プロパティの設定は省略しています.
残りのお試しクラスFoo
は前回と同じ,実行用のクラスは前々回と同じです.
それを実行すると...
Debug interceptor: count=1 invocation=[Invocation: method=[public void study.Foo.createTable 以下略 Debug interceptor: next returned - Initiating transaction commit Debug interceptor: count=2 invocation=[Invocation: method=[public void study.Foo.insert 以下略 Debug interceptor: next returned - Initiating transaction commit Debug interceptor: count=3 invocation=[Invocation: method=[public java.lang.String study.Foo.query 以下略 Debug interceptor: next returned - Initiating transaction commit Ebihara Debug interceptor: count=4 invocation=[Invocation: method=[public void study.Foo.insert 以下略 - Initiating transaction commit Debug interceptor: count=5 invocation=[Invocation: method=[public java.lang.String study.Foo.query 以下略 Debug interceptor: next returned - Initiating transaction commit Yada Debug interceptor: count=6 invocation=[Invocation: method=[public void study.Foo.update 以下略 - Invoking rollback for transaction on method 'update' in class [study.Foo] due to throwable [java.io.IOException] - Initiating transaction rollback Debug interceptor: count=7 invocation=[Invocation: method=[public java.lang.String study.Foo.query 以下略 Debug interceptor: next returned - Initiating transaction commit Yada
かーんぺき!!
と,こんな感じなんですが,これで早くもトランザクション編は終わりにしようかと.まだプログラマティックにトランザクションを扱うとか,アプリケーションサーバ上で使うとかがドキュメントにはあるのですが,あまり興味をもてないもので.心より恥じる.
ということで,次回からは「Chapter 7. Source Level Metadata Support」に進みたいと思います.「AOPその14 Metadata-Driven Autoproxying」でやったメタデータのことなのかな? それにいきます.