概要
@Transactionalを用いた宣言的トランザクションの使い方についてまとめた。
アノテーションを利用してトランザクション管理を行うことで、煩雑なトランザクションまわりのコードを除外できる。
この記事では、@Transactionalを利用するための必要な準備と使用方法について紹介する。
前提
以下の記事の続きとなる。
概要 トランザクションとは何か、トランザクション管理を行う目的やどのように管理していくのかについてまとめた。 トランザクションとは トランザクションとは、「DBに対する一連の処理」を管理する仕組み。 トランザクシ[…]
事前準備
トランザクション管理を行うために、サービスクラスの用意とトランザクションマネージャの有効化を行う。
サービスクラスの用意
トランザクションの境界は、サービスクラスのメソッドとする。
メソッドが呼ばれたタイミングでトランザクションが開始し、メソッドが終了したタイミングでトランザクションも終了となる。
サービスクラスはDAOクラスと同様に、インターフェースと実装を分けて作成する。
インターフェース
テーブルを参照するメソッドと更新するメソッドを用意した。
StudentService.java
public interface StudentService {
/** 全生徒情報を取得 */
List<Student> findAllStudens();
/** 生徒情報とテスト情報の作成 */
int insertStudentInfo(Student student, List<TestPoint> test) throws Exception ;
}
実装
インターフェースのメソッドを実装する。
StudentServiceImpl.java
@Transactional(readOnly = true)
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
/** 全生徒情報を取得 */
@Override
public List<Student> findAllStudens() {
return studentDao.findAllStudens();
}
/** 生徒情報とテスト情報の作成 */
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 10, readOnly = false,
rollbackForClassName = {"java.lang.Exception"})
@Override
public int insertStudentInfo(Student student, List test) {
// 生徒情報作成
int studentId = studentDao.insertStudentAndGetId(student);
// テスト情報作成
test.forEach(t -> t.setStudentId(studentId));
studentDao.batchInsertTestPoints(test);
return studentId;
}
}
@Service
public class StudentServiceImpl implements StudentService {
Transactionalアノテーションは、クラスまたはメソッドに付与できる。
上記はクラスに付与しており、「readOnly = true」を設定することで、クラス内のメソッドはデフォルトで読み取り専用のトランザクション設定が適用される。
読み取り専用にすることで、ロック等を行う必要がないためパフォーマンスが向上すると言われる。
また、独立性レベル(Isolation)を設定していない場合、DBごとのデフォルトの独立性レベルが適用される。
(OracleはREAD COMMITTED、MySQLはREPEATABLE READ、PostgreSQLはREAD COMMITTEDなど)
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 10, readOnly = false,
rollbackForClassName = {“java.lang.Exception”})
@Override
public int insertStudentInfo(Student student, List<TestPoint> test) throws Exception {
上記はメソッドに対してトランザクション設定を行っている。
クラスに@Transactionalが付与されていても、メソッドのトランザクション設定が優先される。
トランザクションの設定としては、伝搬属性、独立性レベル、タイムアウト、ロールバック対象のチェック例外等を設定している。
ロールバック対象例外はRuntime系のため、もしチェック例外が起こりうる場合は必ずロールバック対象のチェック例外を設定すること。
トランザクションの有効化
@Transactionalを付与してトランザクション管理を有効にするため、必要な設定を追加する。
DIコンテナに登録
トランザクションマネージャをDIコンテナに登録する。
今回はH2DBに合わせて、DataSourceTransactionManagerを登録する。
また、アノテーションを有効化するための設定も合わせてBean定義ファイルに追加する。
applicationContext.xml
<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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- JdbcTemplateを使用する場合 -->
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<!-- NamedParameterJdbcTemplateを使用する場合 -->
<bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<!-- H2 DBのデータソース -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1" />
</bean>
<!-- DB初期化 -->
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:sql/schema.sql" />
<jdbc:script location="classpath:sql/data.sql" />
</jdbc:initialize-database>
<!-- コンポーネントスキャン -->
<context:component-scan base-package="com.example.prototype.biz" />
<!-- トランザクションマネージャ -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- @Transactionアノテーションを利用する ※この設定がないとアノテーションが機能しない -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
<bean id=”transactionManager” class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=”dataSource” ref=”dataSource” />
</bean>
トランザクション管理を行うために、トランザクションマネージャをDIコンテナに登録する。
使用するDBに合わせて、PlatformTransactionManagerの実装クラスを記述すること。
<tx:annotation-driven transaction-manager=”transactionManager”/>
上記により、アノテーションが有効になる。
「bean id=”transactionManager”」としている場合、「transaction-manager=”transactionManager”」の記述は省略可能。
以上により、Transactionalアノテーションを利用した宣言的トランザクション管理が可能となる。