トランザクション
コミットとロールバックです。
「トランザクション」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
トランザクションとは
トランザクションは、複数のSQL操作を1つの、すべて成功するかすべて失敗するかの単位にまとめます。
すべてのステートメントが成功すれば変更を永続化し(コミット)、何かが失敗すればすべて取り消します(ロールバック)。これによりデータベースの一貫性が保たれます。
1分でわかるACID
トランザクションは、次のACID特性を保証します。
- 原子性(Atomicity) — すべて成功するか、すべて失敗します。
- 一貫性(Consistency) — DBを、ある有効な状態から別の有効な状態へ移行させます。
- 分離性(Isolation) — 同時実行されるトランザクションが互いのデータを壊しません。
- 永続性(Durability) — いったんコミットされると、クラッシュしても変更が保持されます。
自動コミットモード
デフォルトでは、JDBC接続は自動コミットモードで動作します。つまり、各ステートメントが独立したトランザクションとなり、すぐにコミットされます。
単一のステートメントなら問題ありませんが、複数の書き込みをまとめて成功させる必要がある場合には役に立ちません。トランザクションを自分で制御するには、自動コミットを無効にします。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/bank";
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
System.out.println("Default auto-commit: " + conn.getAutoCommit());
conn.setAutoCommit(false);
System.out.println("Now manual: " + conn.getAutoCommit());
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}コミットとロールバック
自動コミットを無効にすると、トランザクションの境界を自分で決められます。
conn.commit()は保留中のすべての変更を永続化します。conn.rollback()は変更を破棄します。
典型的な例は送金です。1つの口座から引き落とし、別の口座に入金します。両方とも実行されるか、どちらも実行されない必要があります。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/bank";
String debit = "UPDATE account SET balance = balance - ? WHERE id = ?";
String credit = "UPDATE account SET balance = balance + ? WHERE id = ?";
try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
conn.setAutoCommit(false);
try (PreparedStatement d = conn.prepareStatement(debit);
PreparedStatement c = conn.prepareStatement(credit)) {
d.setInt(1, 100); d.setInt(2, 1); d.executeUpdate();
c.setInt(1, 100); c.setInt(2, 2); c.executeUpdate();
conn.commit();
System.out.println("Transfer committed");
} catch (SQLException ex) {
conn.rollback();
System.out.println("Rolled back: " + ex.getMessage());
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}失敗時にロールバックするパターン
覚えておくべき構成は次のとおりです。
- 自動コミットを無効にします。
- 内部の
tryブロック内で処理を実行します。 - 成功したら
commit()を呼び出します。 catchではrollback()を呼び出し、例外を再スローするかログに記録します。
ロールバックを行わないと、失敗によってデータベースが中途半端に更新された状態になる可能性があります。
原子性のシミュレーション
ここでは、すべて成功するかすべて失敗するかという考え方を、単体で実行できる通常のJavaでモデル化しています。スナップショットに対して2つの更新を適用し、どちらか一方でも失敗したらスナップショットを復元します。
public class Main {
public static void main(String[] args) {
int[] balances = {500, 300};
int[] backup = balances.clone();
try {
balances[0] -= 200;
if (balances[0] < 0) throw new IllegalStateException("insufficient funds");
balances[1] += 200;
System.out.println("Commit: " + balances[0] + ", " + balances[1]);
} catch (RuntimeException e) {
balances = backup;
System.out.println("Rollback: " + balances[0] + ", " + balances[1]);
}
}
}セーブポイント
セーブポイントは、トランザクション内に設定するマーカーです。トランザクション全体を破棄せずに、その位置までロールバックできます。
Savepoint sp = conn.setSavepoint()conn.rollback(sp)は、その位置より後の処理だけを取り消します。
長いトランザクションで部分的に復旧したい場合に便利です。
分離レベル
分離レベルは、あるトランザクションが別のトランザクションのコミットされていない処理をどこまで見られるかを制御します。JDBCでは conn.setTransactionIsolation(...) によってレベルを指定します。
TRANSACTION_READ_COMMITTED— 一般的なデフォルトです。TRANSACTION_REPEATABLE_READTRANSACTION_SERIALIZABLE— 最も強力ですが、最も低速です。
分離レベルを高くすると異常を防げますが、同時実行性は低下します。
読み取り時の異常
分離レベルが低いと、次のような現象が起こる可能性があります。
- ダーティーリード — 別のトランザクションのコミットされていない変更を読み取ることです。
- ノンリピータブルリード — 2回の読み取りの間に行が変更されることです。
- ファントムリード — 以前のクエリに一致する新しい行が現れることです。
処理が許容できない異常を防げる範囲で、最も低いレベルを選択してください。
自動コミットの復元
コネクションプールから接続を借りた場合は、返却する前に元の状態へ戻してください。
手動トランザクションの後は、conn.setAutoCommit(true) を再度設定するか、借りたときに確認した値に戻してください。そうしないと、次に使うコードが設定の影響を受けてしまいます。
トランザクションを短く保つ
トランザクションは、コミットまたはロールバックされるまでロックを保持します。長いトランザクションは他の処理をブロックし、デッドロックのリスクを高めます。
- オープン中のトランザクション内で、時間のかかるネットワーク呼び出しやユーザーへの入力要求を行わないでください。
- まずデータを集め、その後で接続、書き込み、コミットを素早く行います。
短く目的を絞ったトランザクションのほうが、はるかに効率よくスケールします。
クイックチェック
connection.rollback() を呼び出すと、どのような効果がありますか?
まとめ
トランザクションによって、すべて成功するかすべて失敗するかという安全性を確保できます。
setAutoCommit(false)で自動コミットを無効にします。- 成功時には
commit()、失敗時にはrollback()を呼び出します。 - 部分的なロールバックにはセーブポイントを使用します。
- 分離レベルを調整して、正確性と同時実行性のバランスを取ります。
- プールされた接続を返却する前に、自動コミットを元に戻します。
よくある質問
「トランザクション」レッスンは無料ですか?
はい。「トランザクション」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「トランザクション」で何を学びますか?
コミットとロールバックです。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「トランザクション」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。