W3docs

Java JDBC-Transaktionen

Datenbanktransaktionen in Java JDBC steuern mit setAutoCommit, commit, rollback und Savepoints.

Eine Transaktion fasst mehrere SQL-Anweisungen zu einer Alles-oder-Nichts-Einheit zusammen: Entweder werden alle Änderungen übernommen, oder keine. Das klassische Beispiel ist eine Banküberweisung — ein Konto wird belastet, ein anderes gutgeschrieben — wobei das Ausführen einer Seite ohne die andere Geld vernichten oder erschaffen würde. JDBC steuert Transaktionen über das Connection-Objekt.

Dieses Kapitel erklärt, wie man eine Transaktion durch Deaktivieren des Auto-Commits öffnet, wie commit() und rollback() funktionieren, wie Savepoints es ermöglichen, einen Teil einer Transaktion rückgängig zu machen, und wie Isolationsstufen Konsistenz gegen Nebenläufigkeit abwägen.

Auto-Commit ist standardmäßig aktiviert

Eine neue Verbindung läuft im Auto-Commit-Modus: Jede Anweisung wird sofort nach ihrer Ausführung übernommen. Das ist für einzelne Anweisungen in Ordnung, aber nicht für mehrsprachige Einheiten. Um eine Transaktion zu öffnen, schaltet man Auto-Commit aus:

conn.setAutoCommit(false);   // begin a transaction
try {
  // ... several statements ...
  conn.commit();             // make all changes permanent
} catch (SQLException e) {
  conn.rollback();           // undo everything since the last commit
  throw e;
} finally {
  conn.setAutoCommit(true);  // restore default for the pool
}

commit und rollback

commit() macht alle Änderungen seit dem Beginn der Transaktion dauerhaft und für andere sichtbar. rollback() verwirft sie alle. Die goldene Regel: Bei Erfolg committen, bei jeder Ausnahme zurückrollen. Das Vergessen des Rollbacks lässt die Verbindung mit gehaltenen Sperren und einer halbfertigen Transaktion zurück.

Eine Beispiel-Überweisung

Die Überweisung verwendet zwei PreparedStatement-Objekte, führt beide UPDATEs aus und committet erst, wenn beide ausgeführt wurden. Wenn eines eine Ausnahme wirft, rollt der catch-Block alles zurück, damit kein Geld erzeugt oder verloren geht:

conn.setAutoCommit(false);
try (PreparedStatement debit = conn.prepareStatement(
        "UPDATE acct SET bal = bal - ? WHERE id = ?");
     PreparedStatement credit = conn.prepareStatement(
        "UPDATE acct SET bal = bal + ? WHERE id = ?")) {
  debit.setBigDecimal(1, amount); debit.setInt(2, fromId); debit.executeUpdate();
  credit.setBigDecimal(1, amount); credit.setInt(2, toId); credit.executeUpdate();
  conn.commit();
} catch (SQLException e) {
  conn.rollback();
  throw e;
}

Savepoints: partieller Rollback

Ein Savepoint ist eine Markierung innerhalb einer Transaktion, zu der man zurückrollen kann, um spätere Arbeiten rückgängig zu machen, während frühere Arbeiten erhalten bleiben. Das ist nützlich, wenn ein optionaler Schritt innerhalb einer größeren Einheit fehlschlagen könnte, ohne dass man die gesamte Transaktion aufgeben möchte:

Savepoint sp = conn.setSavepoint("afterDebit");
// ... risky step ...
conn.rollback(sp);   // undo back to the savepoint, not the whole transaction

Zwei wichtige Regeln: Das Zurückrollen zu einem Savepoint beendet die Transaktion nicht — danach ist noch commit() oder ein vollständiges rollback() erforderlich — und man sollte nicht mehr benötigte Savepoints mit conn.releaseSavepoint(sp) freigeben, um Datenbankressourcen zu schonen. Savepoints existieren nur, wenn Auto-Commit deaktiviert ist.

Isolationsstufen

setTransactionIsolation(...) wägt Konsistenz gegen Nebenläufigkeit ab. Von schwächsten bis stärksten: READ_UNCOMMITTED (sieht uncommittete "schmutzige" Zeilen anderer), READ_COMMITTED (der übliche Standard), REPEATABLE_READ (Wiederholungen sehen dieselben Zeilen) und SERIALIZABLE (Transaktionen verhalten sich, als würden sie nacheinander ausgeführt). Stärkere Stufen verhindern mehr Anomalien, erlauben aber weniger Parallelisierung.

Die Anomalien, gegen die jede Stufe schützt, in der Reihenfolge:

  • Dirty Read — Lesen einer Zeile, die eine andere Transaktion geändert, aber noch nicht committet hat. Verhindert ab READ_COMMITTED aufwärts.
  • Non-Repeatable Read — Erneutes Lesen derselben Zeile und Erhalten eines anderen Werts, weil eine andere Transaktion zwischenzeitlich eine Änderung committet hat. Verhindert ab REPEATABLE_READ aufwärts.
  • Phantom Read — Erneutes Ausführen derselben Abfrage und Erhalten zusätzlicher Zeilen, weil eine andere Transaktion passende Zeilen eingefügt hat. Nur durch SERIALIZABLE verhindert.

Die Stufe vor Beginn der Arbeit festlegen und mit DatabaseMetaData.supportsTransactionIsolationLevel(...) prüfen, was die Datenbank tatsächlich unterstützt — nicht jeder Treiber erfüllt jede Stufe.

Ein Beispielprogramm: Isolationsstufen und die Arbeitseinheit

Dieses Programm gibt die vier Isolationsstufen-Konstanten in aufsteigender Stärke aus und modelliert dann eine Überweisung als atomare Einheit — zwei Updates werden vorbereitet und entweder beide committet oder beide zurückgerollt — und spiegelt dabei den setAutoCommit/commit/rollback-Ablauf ohne eine echte Datenbank wider.

java— editable, runs on the server

Was man aus dem Programmablauf mitnehmen kann:

  • Die Isolationskonstanten steigen mit der Stärke: READ_UNCOMMITTED (1), READ_COMMITTED (2), REPEATABLE_READ (4), SERIALIZABLE (8). Man übergibt eine davon an setTransactionIsolation, um festzulegen, wie viel Nebenläufigkeitsanomalie man toleriert.
  • Die Überweisung bereitet beide Updates vor dem Committen vor. Das ist das Wesen einer Transaktion: Die zwei UPDATEs sind eine Einheit, und commit() erfolgt erst, wenn beide bereit sind.
  • Der commit()-Schritt verschiebt hier beide Anweisungen gemeinsam von pending nach committed — und modelliert so, wie die Datenbank alle Änderungen zum selben Zeitpunkt dauerhaft und sichtbar macht, niemals nur zur Hälfte.
  • Der catch-Block leert pending — das Äquivalent zu rollback(). Die Lektion ist die Disziplin: Jede Ausnahme innerhalb der Transaktion muss zu einem Rollback führen, sonst bleibt die Datenbank in einem halb angewendeten Zustand.
  • transfer applied: true spiegelt einen erfolgreichen Commit wider. Wenn man die Logik so anpasst, dass die Prüfung fehlschlägt, sieht man einen Rollback ohne angewendete Änderungen — genau die Alles-oder-Nichts-Garantie, für die eine Transaktion existiert.

Übungsaufgaben

Übung
Du setzt conn.setAutoCommit(false), führst zwei UPDATE-Anweisungen aus, und die zweite wirft eine SQLException. Was muss dein catch-Block tun, um die Datenintegrität zu wahren?
Du setzt conn.setAutoCommit(false), führst zwei UPDATE-Anweisungen aus, und die zweite wirft eine SQLException. Was muss dein catch-Block tun, um die Datenintegrität zu wahren?

Verwandte Themen

  • JDBC Connection — wo Auto-Commit, commit und rollback angesiedelt sind.
  • PreparedStatement — die sichere Methode zum Ausführen parametrisierter Updates innerhalb einer Transaktion.
  • Batch Processing — viele Anweisungen gruppieren; Batches und Transaktionen werden oft zusammen verwendet.
  • DatabaseMetaData — abfragen, welche Isolationsstufen der Treiber unterstützt.
Was this page helpful?