Spring Transaction Management: @Transactional In-Depth を読む Part 2

Part 1 のつづき。

元記事は以下。

www.marcobehler.com

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 でトランザクション管理をするもっとも初歩的な方法は、 TransactionTemplatePlatformTransactionManager を使うやり方である。

そのコード例は以下である。

@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>

上記のコードは XMLAOP advice (Aspect Oriented Programming) を定義している。 この定義した advice の ID が txAdvice である。

以下のコードで txAdviceUserService クラスのメソッドに適用している。

<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)
        }
    }
}
  1. の部分は JDBC の標準的なトランザクション管理のコードである。
  2. の部分はあなたが書いたコードである。

この例はちょっとした魔法のようであるが、次は Spring がどうやってこのコードを生成したのか見ていこう。

眠くなってきたので Part 2 はこのあたりで終わりにする。
ここから濃い話が続く感じなので気が向いたら読もう。
今のところ @Transactional をメソッドに付けると、その中身がトランザクションとして実行されることは分かった。