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

トランザクション管理って難しい。
Spring を用いてトランザクション管理を解説している以下の記事がよさげだったので整理しながら読んでみる。

www.marcobehler.com

この記事を読み終わるころには、Spring の @Transactional アノテーションがどのように動作するのか理解できるようになるらしい。知りたい。ただし、ACIDつまりトランザクションとは何であるのかは理解していることを想定している。

なので簡単に ACID やトランザクションという概念についてまとめておく。

  • データベース操作の列のうち、ACID を満たすものを「トランザクション」と呼ぶ。
  • ACID は atomicity, consistency, isolation, durability の4つの性質のことである。抜粋すると、操作列が途中で失敗したらロールバックできるし、複数の操作の並列アクセスによる排他の問題は発生しないような性質。
  • すこし視点を変えると、一連の操作を ACID なものとして扱いたいとき、一連の操作が「トランザクションであること」を明示してやることになる。そうすればロールバック排他制御も自前で実装しなくてよい。

以下、元記事の章ごとに重要そうなところをメモってゆく。

Introduction

この解説では、Spring 特有のトランザクション管理の方法から始めるのではなく、 JDBC transaction management のおける古典的なトランザクション管理の方法から解説する。
Spring の @Transactional アノテーションは、この古典的な考えを基礎としているので、まずはその基礎を知ることが重要である。

How plain JDBC Transaction Management works

How to start, commit or rollback JDBC transactions

Spring の @Transactional アノテーションでも、Hibernate でも、その他のデータベースライブラリであっても、結局のところ「データベーストランザクションを開いて閉じる」という点は同じである。この「開いて閉じる」ことを「管理する」と呼ぶ。

JDBC におけるトランザクション管理をするコードの例を以下に示す。

import java.sql.Connection;

Connection connection = dataSource.getConnection(); // (1)

try (connection) {
    connection.setAutoCommit(false); // (2)
    // execute some SQL statements...
    connection.commit(); // (3)

} catch (SQLException e) {
    connection.rollback(); // (4)
}
  1. データベースへ接続する。
  2. このメソッド名からは直感的でないが、setAutoCommit(false) の実行が Java においてトランザクションを開始する唯一の方法である。このメソッドを実行すると、あなたはトランザクションのマスターになれるわけだ。
  3. トランザクションをコミットする。
  4. 例外が発生したらロールバックする。

ざっくり言うと、@Transactional アノテーションが用いられたとき、Spring はこの4つの処理をやっているだけである。
@Transactional アノテーションがどのように動作するか詳しく見る前に、知っておくべきことを先に説明する。

How to use JDBC isolation levels and savepoints

Spring の @Transactional アノテーションをすでに使ったことがある人であれば、以下のようなコードを見たことがあるかもしれない。

@Transactional(propagation=TransactionDefinition.NESTED,
               isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)

このアノテーションは以下の基本的な JDBC のコードになるだけである。

import java.sql.Connection;
// isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); // (1)
// propagation=TransactionDefinition.NESTED
Savepoint savePoint = connection.setSavepoint(); // (2)
...
connection.rollback(savePoint);
  1. Spring の TransactionDefinition.ISOLATION_READ_UNCOMMITTED は、JDBC ではデータベースの接続の isolation level として反映される。
  2. Spring の TransactionDefinition.NESTED は、JDBC ではデータベースの savepoint として表現されるだけである。

こんな感じで JDBC を考えれば、Spring は全然難しいことをしてるわけではない!

眠くなったので今日はここまで。続きは後日。