ホーム › データベース(JDBC) › DB-03
DB-03: トランザクション処理(commit / rollback)
トランザクションとは、複数のデータベース操作を「全て成功」か「全て失敗」のどちらかにまとめる仕組みです。 銀行の送金のように「送金元の引き落とし」と「送金先への入金」を必ずセットで完了させたい処理に不可欠です。
ACID 特性とトランザクション
トランザクションは ACID と呼ばれる4つの性質を保証します。 これにより、複数の処理が途中で失敗しても DB が矛盾した状態にならないことが保証されます。
ACID 特性
| 英語 | 日本語 | 意味 |
|---|---|---|
Atomicity | 原子性 | 全て成功するか、全て取り消されるか、どちらかしかない |
Consistency | 一貫性 | トランザクション前後でデータの整合性が保たれる |
Isolation | 分離性 | 複数のトランザクションが互いに影響を与えない |
Durability | 永続性 | コミット後のデータはシステム障害が起きても失われない |
トランザクション分離レベル
分離レベルを上げると整合性が高まりますが、同時実行性(スループット)が低下します。 多くの DB では READ_COMMITTED がデフォルトです。
| 分離レベル | ダーティリード | ファジーリード | ファントムリード |
|---|---|---|---|
READ_UNCOMMITTED | 発生あり | 発生あり | 発生あり |
READ_COMMITTED | 防止 | 発生あり | 発生あり |
REPEATABLE_READ | 防止 | 防止 | 発生あり |
SERIALIZABLE | 防止 | 防止 | 防止 |
サンプルコード
Java 8 では setAutoCommit(false) → 処理 → commit() または rollback() → finally で setAutoCommit(true) のパターンが基本です。rollback() も try-catch で囲んで例外を握りつぶさないよう注意してください。
よくあるミス・注意点
⚠️ setAutoCommit(false) したら必ず commit() か rollback() を呼ぶ
setAutoCommit(false) にしたままcommit() もrollback() も呼ばずに接続を閉じると、 DB によってはトランザクションが宙ぶらりんになり、ロックが長時間保持されたままになります。 必ず finally ブロックで処理を完結させましょう。
⚠️ Connection を返す前に setAutoCommit(true) でリセットする
コネクションプールを使う環境では、一度使った Connection が他のリクエストで再利用されます。setAutoCommit(false) の状態が残ったまま返却されると、 次のリクエストが意図せずトランザクション中になります。finally で必ずsetAutoCommit(true) に戻してください。
⚠️ 分離レベルを上げるとデッドロックが発生しやすくなる
SERIALIZABLE など高い分離レベルでは複数のトランザクションが互いにロックを待ち合い、 デッドロック(永久に進まない状態)が発生しやすくなります。 実務では READ_COMMITTED を使い、 必要な箇所だけ SELECT FOR UPDATE などで明示的にロックするのが一般的です。
⚠️ 長いトランザクションは他の処理をブロックする
トランザクション中は関連する行やテーブルにロックがかかります。 処理が長くなるほど他のリクエストを待たせる時間が増えます。 トランザクションの範囲はできるだけ短くし、ループ内で大量に処理するときは一定件数ごとに commit() するバッチコミットを検討しましょう。
テストする観点
- 正常送金後、送金元の残高が減り、送金先の残高が増えていること
- 残高不足で失敗したとき、ロールバック後に両方の残高が変わっていないこと
- コミット後に接続を閉じて再取得しても、データが永続化されていること
- 存在しない送金元 ID を指定したとき、ロールバックが行われること(境界値)
- 送金額が 0 のとき、残高が変わらずコミットされること(境界値)
- rollback() が呼ばれた後、同じ接続で次の処理が正常に動作すること