Transaktionen und auto-commit
Jetzt, wo Sie via PDO verbunden sind, müssen Sie sich bewusst machen, wie PDO
Transaktionen verwaltet, bevor Sie anfangen, Abfragen auszuführen. Falls Sie
noch nie mit Transaktionen zu tun hatten, diese bieten 4 wichtige Features:
Atomizität, Konsistenz (Consistency), Isolation und Dauerhaftigkeit
(Durability) (ACID). Einfach gesagt wird alles in einer Transaktion, auch
wenn es in Einzelschritten ausgeführt wird, garantiert in sicherer Weise in
die Datenbank eingetragen, ohne Beeinträchtigung durch andere Verbindungen,
wenn es abgeschickt wird. Aktivitäten in Transaktionen können auch
automatisch annulliert werden (wenn Sie es noch nicht abgeschickt haben), was
Fehlerbehandlung in Ihren Scripts einfacher macht.
Transaktionen werden typischerweise implementiert, indem Ihre Menge an
Änderungen "aufgespart" wird und dann in einem Rutsch abgearbeitet werden.
Das hat den netten Nebeneffekt, dass die Effizienz der Aktualisierungen
drastisch erhöht wird. In anderen Worten können Transaktionen Ihre Scripts
schneller und möglicherweise auch robuster machen. Aber Sie müssen sie
korrekt verwenden, um davon zu profitieren.
Unglücklicherweise unterstützt nicht jede Datenbank Transaktionen, deswegen
muss PDO in einem "auto-commit" genannten Modus laufen, wenn Sie die
Verbindung zum ersten Mal öffnen. "Auto-commit" bedeutet, dass jede Abfrage,
die Sie ausführen ihre eigene implizite Transaktion besitzt, wenn die
Datenbank das unterstützt, oder keine Transaktion, wenn die Datenbank keine
Transaktionen unterstützt. Wenn Sie eine Transaktion benötigen, müssen Sie
eine mit der Methode PDO::beginTransaction()
initiieren. Wenn der zu Grunde liegende Treiber keine Transaktionen
unterstützt, wird eine PDOException
geworfen (unbeachtet
Ihrer Einstellungen zur Fehlerbehandlung: dies ist immer eine ernste
Fehlerbedingung). Wenn Sie dann in einer Transaktion sind, können Sie
PDO::commit() oder
PDO::rollBack() benutzen, um die Transaktion
abzuschließen, abhängig vom Erfolg des Codes, den Sie während der Transaktion
ausgeführt haben.
Warnung
PDO überprüft nur auf Treiberebene auf Transaktionsfähigkeit. Wenn bestimmte
Laufzeitbedingunen ergeben, dass Transaktionen nicht verfügbar sind, wird
PDO::beginTransaction() trotzdem ohne Fehler true
zurückgeben, wenn der Server die Anfrage zum Start einer Transaktion
akzeptiert.
Ein Beispiel wäre es, Transaktionen auf MyISAM-Tabellen in einer
MySQL-Datenbank zu verwenden
Warnung
Implicit Commits with DDL Statements:
Some databases automatically issue an implicit COMMIT
when a database definition language (DDL) statement, such as DROP
TABLE
or CREATE TABLE
, is executed within a
transaction. This means that any prior changes made within the same
transaction will be automatically committed and cannot
be rolled back.
MySQL
and Oracle
are example databases
that exhibit this behavior.
Beispiel #1 Implicit Commit Example
<?php
$pdo->beginTransaction();
$pdo->exec("INSERT INTO users (name) VALUES ('Rasmus')");
$pdo->exec("CREATE TABLE test (id INT PRIMARY KEY)"); // Implicit COMMIT occurs here
$pdo->rollBack(); // This will NOT undo the INSERT/CREATE for MySQL or Oracle
?>
Best Practice: Avoid executing DDL statements inside
transactions if using databases that enforce this behavior. If necessary,
separate DDL operations from transactional logic.
Wenn das Script endet oder die Verbindung im Begriff ist, geschlossen zu
werden und Sie eine Transaktion ausstehen haben, wird PDO automatisch einen
Rollback durchführen. Dies ist eine Sicherheitsmaßnahme, um Inkonsistenzen in
dem Fall, dass das Script unerwartet beendet wird, zu vermeiden - wenn Sie
die Transaktion nicht explizit ausgeführt haben, wird angenommen, dass etwas
schiefgegangen ist, deswegen wird zur Sicherheit Ihrer Daten ein Rollback
durchgeführt.
Warnung
Der automatische Rollback wird nur durchgeführt, wenn Sie die Transaktion
per PDO::beginTransaction() starten. Wenn Sie manuell
eine Abfrage ausführen, die eine Transaktion startet, kann PDO nichts davon
wissen und kann deswegen auch keinen Rollback durchführen, wenn etwas
schiefgeht.
Beispiel #2 Mehrere Abfragen in einer Transaktion
Im folgenden Beispiel nehmen wir an, dass wir einen Satz von Einträgen für
einen neuen Angestellten eintragen wollen, dem die ID-Nummer 23 zugeordnet
wurde. Zusätzlich zur Angabe der Basisdaten für diese Person müssen wir
auch ihr Gehalt festhalten. Es ist ziemlich einfach, zwei getrennte
Aktualisierungen durchzuführen, aber indem wir sie in
PDO::beginTransaction() und
PDO::commit() einschließen, garantieren wir, dass
niemand anderes diese Änderungen sieht, bis sie komplett sind. Wenn etwas
schiefgeht, wird der catch
-Block alle seit Beginn der
Transaktion durchgeführten Änderungen rückgängig machen und dann eine
Fehlermeldung ausgeben.
<?php
try {
$dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
array(PDO::ATTR_PERSISTENT => true));
echo "Connected\n";
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();
} catch (Exception $e) {
$dbh->rollBack();
echo "Failed: " . $e->getMessage();
}
?>
Sie sind nicht darauf beschränkt, Änderungen in einer Transaktion
durchzuführen. Sie können auch komplexe Abfragen ausführen um Daten zu
erhalten, und diese Informationen etwa dazu benutzen, mehr Änderungen und
Abfragen zu erzeugen. Während die Transaktion aktiv ist, kann garantiert
niemand anderes Änderungen durchführen, während Sie bei der Arbeit sind. Um
ehrlich zu sein, stimmt das nicht 100%, aber es ist für den Anfang
ausreichend, falls Sie noch nie von Transaktionen gehört haben.