[spring]10.3 Understanding the Spring Framework transaction abstraction
10.3 Understanding the Spring Framework transaction abstraction
Springのトランザクション管理における中心概念は、「TransactionStrategy」である。具体的にはorg.springframework.transaction.PlatformTransactionManagerを指す。
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}PlatformTransactionManagerはSPIであり、かつ、ソースコードでトランザクション制御する場合はAPIになる。PlatformTransactionManagerはインタフェースなので、簡単にモックに差し替えることができる。PlatformTransactionManagerは(アプリケーションでJTAを使う場合とは異なり)JNDIルックアップは不要。PlatformTransactionManagerは他のBeanと同様にSpringIOCコンテナを使って定義できるので簡単にルックアップできる。なのでトランザクショナルなコードはJTAを直接使っている場合と比べて、ずっとテストしやすい。
getTransactionメソッドは、引数TransactionDefinitionに対応するTransactionStatusオブジェクトを返す。TransactionStatusは新規のトランザクションもしくはすでに開始されているトランザクション*1を表す。TransactionStatusはJTAのように実行スレッドと関連付けられる。
TransactionDefinitionインタフェースは以下を定義する。
- Isolation(トランザクション分離レベル):いわゆるトランザクション分離レベルをそい呈することができる。
- Propagation(トランザクションの伝搬):普通はすべてのコードはトランザクション制御下で動く。ただしトランザクションがすでに開始している場合についてはその場合の振る舞いを指定することができる。例えばすでに開始済みのトランザクション配下で動作させたり、既存のトランザクションを中断し新たにトランザクションを開始するなどである。Springは、EJBのCMTトランザクションと同様のトランザクション伝搬機能を提供する。
- タイムアウト:トランザクションタイムアウトの指定が可能。
- リードオンリー:書き込みができないトランザクションを定義できる。Hibernateを使う場合に便利なことがある。
TransactionStatusインタフェースは、以下のような定義になっており、トランザクションの状態確認や制御をすることができる。
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}宣言的トランザクションでも、プログラムでトランザクション制御する場合でも、適切なPlatformTransactionManagerを定義する必要がある。普通はSpringIOCを用いてPlatformTransactionManagerを定義する。PlatformTransactionManagerの実装クラスは、JDBCやJTA、Hibernateなど制御対象ごとに定義される。次の例はJDBCの場合の定義例である。
・データソース
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
・PlatformTransactionManager
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
もしJTAを使って制御する場合は、代わりに以下のように記述する。
<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee
">http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"><jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/> definitions here -->
</beans>
JTAはグローバルトランザクションの機構なので、個別のデータソースをJtaTransactionManagerに紐付ける必要はない。
以下はHibernateの場合の定義例。
<!--LocalSessionFactoryBean : Hibernate Sessionインスタンスを取得するためのBean. -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
</value>
</property>
</bean><bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Hibernateの場合もJTAトランザクションは以下で動かす場合は、
の定義で良い。
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
どのような定義であってもアプリケーションのソースコードを変更する必要はない。環境がローカルからグローバルトランザクションに変わっても、設定を変えるだけで良い。
*1:コールスタックに乗っている