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 transactionZwei 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_COMMITTEDaufwä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_READaufwärts. - Phantom Read — Erneutes Ausführen derselben Abfrage und Erhalten zusätzlicher Zeilen, weil eine andere Transaktion passende Zeilen eingefügt hat. Nur durch
SERIALIZABLEverhindert.
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.
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 ansetTransactionIsolation, 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, undcommit()erfolgt erst, wenn beide bereit sind. - Der
commit()-Schritt verschiebt hier beide Anweisungen gemeinsam vonpendingnachcommitted— und modelliert so, wie die Datenbank alle Änderungen zum selben Zeitpunkt dauerhaft und sichtbar macht, niemals nur zur Hälfte. - Der
catch-Block leertpending— das Äquivalent zurollback(). 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: truespiegelt 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
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.