Springの@Transactional徹底解説!トランザクションの伝播・分離レベル・タイムアウトの基本
生徒
「Springの@Transactionalって、どうやって使えばいいんですか?トランザクションって正直難しそうで…」
先生
「確かに最初は戸惑いますよね。でも実は、@Transactionalを使えば、データベースの整合性を保つ処理が簡単に実現できますよ。」
生徒
「伝播とか分離レベルとか、設定項目もたくさんあって不安です…」
先生
「それなら、初心者でも理解しやすいように、基礎から丁寧に説明していきましょう!」
1. Springにおけるトランザクション管理の基本
トランザクションとは、データベースへの操作をひとつのまとまりとして扱い、すべて成功したときだけ確定し、失敗したときには元に戻す仕組みです。Springでは、@Transactionalアノテーションを使うことで、自動的にトランザクション管理が行えるようになります。
これにより、複数の処理を安全にまとめて実行し、データの整合性を保つことができます。たとえば、ユーザー登録時にユーザー情報と履歴を同時に保存する処理などが該当します。
2. @Transactionalの基本的な使い方
まずは基本の書き方を見てみましょう。@Transactionalをメソッドやクラスに付けるだけで、トランザクションが自動的に開始・コミット・ロールバックされます。
@Service
public class UserService {
@Transactional
public void registerUser(User user) {
userRepository.save(user);
logRepository.save(new Log("ユーザー登録"));
}
}
この例では、ユーザーとログの登録が両方成功した場合のみ、データベースに反映されます。どちらかで例外が発生したら、自動的にロールバックされるのが特徴です。
3. トランザクションの伝播(propagation)の種類と意味
@Transactionalには、propagation(伝播)という設定があります。これは「すでにトランザクションが存在している場合に、どう振る舞うか」を指定します。
代表的な伝播タイプ:
- REQUIRED(デフォルト):既存のトランザクションがあれば参加、なければ新規作成
- REQUIRES_NEW:常に新しいトランザクションを開始(既存は一時中断)
- MANDATORY:既存トランザクションがないと例外
- NEVER:トランザクションがあると例外
- NESTED:ネストされたトランザクション(JDBCやDBによる)
たとえばログだけ別トランザクションにしたいときにはREQUIRES_NEWを使います。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(Log log) {
logRepository.save(log);
}
4. 分離レベル(isolation)の基本と使いどころ
分離レベルは、同時に実行されるトランザクション同士のデータの見え方を制御します。具体的には、読み取り時に他のトランザクションの未確定データを見えるようにするかどうかです。
Springの@Transactionalでは、以下の分離レベルが指定できます:
- DEFAULT:データベースのデフォルト設定に従う
- READ_UNCOMMITTED:未コミットデータも見える(ダーティリード)
- READ_COMMITTED:コミット済みのデータのみ(一般的)
- REPEATABLE_READ:同一トランザクション内では同じ結果が保証される
- SERIALIZABLE:完全に直列化、最も安全だが最も重い
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readData() {
// 安全な読み取り処理
}
基本的にはREAD_COMMITTEDやREPEATABLE_READが使われます。
5. タイムアウト(timeout)の設定と注意点
トランザクションが長時間ロックを保持しないように制限できるのがtimeout設定です。秒数で指定し、指定時間内に処理が終わらないとTransactionTimedOutExceptionがスローされます。
@Transactional(timeout = 5)
public void longProcess() {
// 5秒以内に完了しないと例外
}
タイムアウトを設定することで、ロック待ちによる遅延や、パフォーマンス劣化を防ぐことができます。
6. rollbackFor属性による例外制御
@Transactionalでは、どの例外が発生したときにロールバックするかも指定できます。通常はRuntimeException系でロールバックされますが、それ以外の例外でもロールバックしたい場合はrollbackForを使います。
@Transactional(rollbackFor = { IOException.class, SQLException.class })
public void riskyMethod() throws IOException {
// 例外発生時にロールバック
}
逆に、ロールバックさせたくない例外がある場合はnoRollbackForも指定できます。
7. トランザクションの確認とログの出力方法
Springでは、トランザクションの開始・コミット・ロールバックのタイミングをログで確認できます。設定ファイルでログレベルをDEBUGにすると、詳細な情報が出力されます。
logging.level.org.springframework.transaction=DEBUG
これにより、思った通りにトランザクションが制御されているかを確認できます。特に分離レベルや伝播に関するバグを追うときに有効です。