「Spring」 DataSourceTransactionManager での宣言的トランザクション

DataSourceTransactionManager で宣言的トランザクションを使ってみたので、メモしておきます。

DataSourceTransactionManager は単一のデータソースに対するトランザクションマネージャになります。

・DataSourceTransactionManager (Spring Framework 3.2.13.RELEASE API)
< http://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/jdbc/datasource/DataSourceTransactionManager.html >

宣言的トランザクションには、Bean 定義ファイルを使用するのと、アノテーションでやる方法がありますが、今回はそれぞれ試してみました。

ドキュメントは以下あたりです。

Spring Framework Reference Documentation
< http://docs.spring.io/spring/docs/3.2.13.RELEASE/spring-framework-reference/htmlsingle/#transaction >

1. Bean 定義ファイルを使用する方法

以下の感じで DAO クラス、Main クラス、Bean 定義ファイルを作成することでできます。

■ SampleDAO.java

package com.example.datasource;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class SampleDAO {
	
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	public void select() {
		List<Map<String, Object>> result = jdbcTemplate.queryForList("select * from t1");
		for (Map<String, Object> r : result) {
			System.out.println(r.get("id").toString() + " : " + r.get("name").toString());
		}
	}
	
	public void insert() {
		int result = jdbcTemplate.update("insert into t1 values (3, 'oro')");
	}
//      ( *1 )
//	public void insert() throws DAOException {
//		throw new DAOException();
//	}
}

■ Main.java

package com.example.datasource;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
	public static void main(String[] args) {
		try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dataSource.xml")) {
			SampleDAO dao = context.getBean(SampleDAO.class);
			dao.insert();
                        //(*2)
			//try {
			//	dao.insert();
			//} catch (DAOException e) {
			//	e.printStackTrace();
			//}
			dao.select();
		}
	}
}

■ dataSource.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:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
 
  <bean id="sampleDAO" class="com.example.datasource.SampleDAO" /> 
              
  <context:property-placeholder location="classpath:jdbc.properties"/>
  
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
  </bean>
 
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>
   
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
      <tx:method name="insert*"
                 propagation="REQUIRED"
                 isolation="READ_COMMITTED"
                 read-only="false"
                 rollback-for="com.example.datasource.DAOException"/>
    </tx:attributes>
  </tx:advice>
  
  <aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.example.datasource.SampleDAO.*(..))" />
  </aop:config>
  
  <context:annotation-config />
  <context:component-scan base-package="com.example.datasource" />
</beans>

今回は SampleDAO の insert メソッドトランザクションの対象にしてます ( propagation 等のトランザクション定義情報は適当です )。
ロールバック対象例外 rollback-for に DAOException を設定しているので、上記のコメントアウト (*1)、(*2) を外してやるとちゃんとロールバックされるはずです。


2. アノテーションを使用する方法

■ SampleDAO.java

package com.example.datasource;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class SampleDAO {
	
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	public void select() {
		List<Map<String, Object>> result = jdbcTemplate.queryForList("select * from t1");
		for (Map<String, Object> r : result) {
			System.out.println(r.get("id").toString() + " : "
					+ r.get("name").toString());
		}
	}

	 @Transactional
	 public void insert() {
	 int result = jdbcTemplate.update("insert into t1 values (3, 'oro')");
	 }

//	@Transactional(rollbackFor=DAOException.class)
//	public void insert() throws DAOException {
//		throw new DAOException();
//	}
}

■ Main.java

  上記と同様なので割愛

■ dataSource.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:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
 
  <bean id="sampleDAO" class="com.example.datasource.SampleDAO" /> 
              
  <context:property-placeholder location="classpath:jdbc.properties"/>
  
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
  </bean>
 
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>
   
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <tx:annotation-driven transaction-manager="txManager"/>
  
  <context:annotation-config />
  <context:component-scan base-package="com.example.datasource" />
</beans>

アノテーションは @Transactional を使用します。トランザクション定義情報も Bean 定義ファイルと同様に設定できますが、上記では設定してません。この場合はデフォルト値が使われるっぽいです。

・Transactional (Spring Framework 3.2.13.RELEASE API)
< http://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/transaction/annotation/Transactional.html >

Bean 定義ファイルでは

<tx:annotation-driven transaction-manager="txManager"/> を設定して、@Transactional が有効になるようにしています。


とりあえず上記でトランザクションが有効になるはずです。

で、DataSourceTransactionManager は log4j でログ吐くコードが入っているので、log4j.properties を以下の感じで作ってやるとトランザクション関連のログが出力されます ( Maven でやってる場合は src/main/resources 配下においてやってください )。

log4j.appender.stderr=org.apache.log4j.ConsoleAppender
log4j.appender.stderr.Target=System.err
log4j.appender.stderr.layout=org.apache.log4j.PatternLayout
log4j.appender.stderr.layout.ConversionPattern=%d %5p %c{1} - %m%n

#log4j.rootLogger=DEBUG,stderr

log4j.logger.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG,stderr


Spring のトランザクションマネージャには他にも HibernateTransactionManager やら JmsTransactionManager、JtaTransactionManager などなど色々あります。基本的に上記と同じ要領だと思ってますが、また今度試してみたいと思います ( まずは分散トランザクションにも対応している JtaTransactionManager からかな・・・ )。

以上です。

[ 環境情報 ]
Windows 7 SP1
Java SE 7 Update 51
Spring Framework 3.2.13
PostgreSQL 9.0.4
PostgreSQL JDBC Driver 9.0-801