Spring Transaction Management: @Transactional In-Depth を読む Part 2
Part 1 のつづき。
元記事は以下。
How Spring’s or Spring Boot’s Transaction Management works
Spring のトランザクション管理や transaction abstraction framework は実際何をしているのか?
JDBC では setAutocommit(false)
がトランザクションを開始する唯一の手段であったが、Spring ではトランザクションを開始するための複数の方法がある。とはいえ、Spring もトランザクションを開始し、コミットまたはロールバックするとうことには変わりない。
How to use Spring’s Programmatic Transaction Management?
実際に使われることはあまりないが、Spring でトランザクション管理をするもっとも初歩的な方法は、 TransactionTemplate
か PlatformTransactionManager
を使うやり方である。
そのコード例は以下である。
@Service public class UserService { @Autowired private TransactionTemplate template; public Long registerUser(User user) { Long id = template.execute(status -> { // execute some SQL that e.g. // inserts the user into the db and returns the autogenerated id return id; }); } }
JDBC の例を比較すると、以下のような特徴がある。
- データベース接続を開始したり終了するような面倒な処理がない。代わりに、Transaction Callbacks を使用している。これは
template.execute
の部分のことを指している。execute
メソッドの引数としてSQL文のみを定義しておけば、適切なトランザクション管理のもとexecute
メソッドをコールバックとして実行してくれる。 SQLException
を catch する例外処理が不要。
これは Spring を用いた手続き的なトランザクション管理の例であった。次に宣言的なトランザクション管理の方法を見よう。
How to use Spring’s XML Declarative Transaction Management?
Spring では、@Transactional
アノテーションが導入される前、XML によってトランザクションが定義されていた。詳細は見ないが、スタート地点として以下のコードを見てみる。
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) --> <tx:advice id="txAdvice" transaction-manager="txManager"> <!-- the transactional semantics... --> <tx:attributes> <!-- all methods starting with 'get' are read-only --> <tx:method name="get*" read-only="true"/> <!-- other methods use the default transaction settings (see below) --> <tx:method name="*"/> </tx:attributes> </tx:advice>
上記のコードは XML で AOP advice (Aspect Oriented Programming) を定義している。
この定義した advice の ID が txAdvice
である。
以下のコードで txAdvice
を UserService
クラスのメソッドに適用している。
<aop:config> <aop:pointcut id="userServiceOperation" expression="execution(* x.y.service.UserService.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="userServiceOperation"/> </aop:config> <bean id="userService" class="x.y.service.UserService"/>
UserService
クラスは以下のような感じである。
public class UserService { public Long registerUser(User user) { // execute some SQL that e.g. // inserts the user into the db and retrieves the autogenerated id return id; } }
以上のコードは Java だけ見ればシンプルに書ける一方で、XML は複雑で冗長なものとなっている。
XML よりよいトランザクションの定義の方法はないか?そこで @Transactional
アノテーションの登場である。
How to use Spring’s @Transactional annotation ( Declarative Transaction Management )
モダンな Spring のコードを見てみよう。
public class UserService { @Transactional public Long registerUser(User user) { // execute some SQL that e.g. // inserts the user into the db and retrieves the autogenerated id // userDao.save(user); return id; } }
ここまでシンプルに書くためには以下の2つのことをしなければならない。
- あなたの Spring Configuration に
@EnableTransactionManagement
アノテーションを付与すること。(Spring Boot は自動的にやってくれている。) - Spring Configuration の中でトランザクションマネージャーを定義すること。
- すると、Spring は自動的に判断して、任意の Bean の
@Transactional
アノテーションのついた public メソッドが1つのトランザクション内で実行されるようになる。
つまり、@Transactional
アノテーションを機能させるために必要なことは以下だけである。
@Configuration @EnableTransactionManagement public class MySpringConfig { @Bean public PlatformTransactionManager txManager() { return yourTxManager; // more on that later } }
具体例には、@Transactional
アノテーションの付いた UserService
のコードは以下に相当する処理をする。
public class UserService { public Long registerUser(User user) { Connection connection = dataSource.getConnection(); // (1) try (connection) { connection.setAutoCommit(false); // (1) // execute some SQL that e.g. // inserts the user into the db and retrieves the autogenerated id // userDao.save(user); <(2) connection.commit(); // (1) } catch (SQLException e) { connection.rollback(); // (1) } } }
この例はちょっとした魔法のようであるが、次は Spring がどうやってこのコードを生成したのか見ていこう。
眠くなってきたので Part 2 はこのあたりで終わりにする。
ここから濃い話が続く感じなので気が向いたら読もう。
今のところ @Transactional
をメソッドに付けると、その中身がトランザクションとして実行されることは分かった。