Der Rahmen hat keine Möglichkeit zu wissen, ob Sie eine Transaktion gestartet haben. Sie können sogar $db->query('START TRANSACTION')
die das Framework nicht kennt, weil es die von Ihnen ausgeführten SQL-Anweisungen nicht analysiert.
Der Punkt ist, dass es in der Verantwortung der Anwendung liegt, zu verfolgen, ob Sie eine Transaktion begonnen haben oder nicht. Das kann das Framework nicht leisten.
Ich weiß, dass einige Frameworks das versuchen und so verrückte Dinge tun, wie z.B. zählen, wie oft man eine Transaktion begonnen hat, und sie erst auflösen, wenn man eine entsprechende Anzahl von Commits oder Rollbacks durchgeführt hat. Aber das ist totaler Blödsinn, weil keine Ihrer Funktionen wissen kann, ob Commit oder Rollback es tatsächlich tun, oder ob sie in einer anderen Schicht der Verschachtelung sind.
(Sieht man, dass ich diese Diskussion schon ein paar Mal geführt habe? :-)
Aktualisierung 1: Vorantreiben ist eine PHP-Datenbank-Zugriffsbibliothek, die das Konzept der "inneren Transaktion" unterstützt, die sich nicht festlegt, wenn Sie sie dazu auffordern. Durch den Beginn einer Transaktion wird lediglich ein Zähler erhöht, und durch die Übergabe/Rückgabe wird der Zähler verringert. Im Folgenden finden Sie einen Auszug aus einem Mailinglisten-Thread, in dem ich einige Szenarien beschreibe, in denen dies nicht funktioniert.
Update 2: Doktrin DBAL verfügt ebenfalls über diese Funktion. Sie nennen es Transaktionsverschachtelung.
Ob es Ihnen gefällt oder nicht, Transaktionen sind "global" und gehorchen nicht der objektorientierten Kapselung.
Problemszenario Nr. 1
Ich rufe commit()
Sind meine Änderungen verbindlich? Wenn ich mich in einer "inneren Transaktion" befinde, sind sie es nicht. Der Code, der die äußere Transaktion verwaltet, könnte einen Rollback durchführen, und meine Änderungen würden ohne mein Wissen oder meine Kontrolle verworfen werden.
Zum Beispiel:
- Modell A: Beginn der Transaktion
- Modell A: einige Änderungen vornehmen
- Modell B: Beginn der Transaktion (silent no-op)
- Modell B: einige Änderungen vornehmen
- Modell B: Commit (stiller No-op)
- Modell A: Rollback (verwirft sowohl die Änderungen von Modell A als auch die von Modell B)
- Modell B: WTF!? Was ist mit meinen Änderungen passiert?
Problemszenario #2
Wenn eine innere Transaktion einen Rollback durchführt, könnte sie legitime Änderungen einer äußeren Transaktion verwerfen. Wenn die Kontrolle an den äußeren Code zurückgegeben wird, geht dieser davon aus, dass seine Transaktion noch aktiv ist und committed werden kann. Mit Ihrem Patch könnten sie commit()
und da die transDepth jetzt 0 ist, würde es stillschweigend die $transDepth
auf -1 setzen und true zurückgeben, nachdem sie nichts begangen haben.
Problemszenario #3
Wenn ich anrufe commit()
o rollback()
wenn keine Transaktion aktiv ist, setzt es die $transDepth
auf -1. Die nächste beginTransaction()
erhöht die Stufe auf 0, was bedeutet, dass die Transaktion weder rückgängig gemacht noch festgeschrieben werden kann. Nachfolgende Aufrufe von commit()
wird die Transaktion einfach auf -1 oder weiter dekrementiert, und Sie werden nie in der Lage sein, eine Zusage zu machen, bis Sie eine weitere überflüssige beginTransaction()
um die Stufe wieder zu erhöhen.
Der Versuch, Transaktionen in der Anwendungslogik zu verwalten, ohne der Datenbank die Buchführung zu überlassen, ist im Grunde genommen eine zum Scheitern verurteilte Idee. Wenn zwei Modelle in einer Anwendungsanforderung eine explizite Transaktionssteuerung verwenden sollen, müssen Sie zwei DB-Verbindungen öffnen, eine für jedes Modell. Dann kann jedes Modell seine eigene aktive Transaktion haben, die unabhängig voneinander bestätigt oder zurückgenommen werden kann.