Ramverket har inget sätt att veta om du startade en transaktion. Du kan till och med använda $db->query('START TRANSACTION')
som ramverket inte skulle känna till eftersom det inte analyserar SQL-satser du kör.
Poängen är att det är ett applikationsansvar att spåra om du har startat en transaktion eller inte. Det är inget ramverket kan göra.
Jag vet att vissa ramverk försöker göra det, och gör cockamamie saker som att räkna hur många gånger du har påbörjat en transaktion, bara lösa det när du har gjort commit eller rollback ett matchande antal gånger. Men detta är helt falskt eftersom ingen av dina funktioner kan veta om commit eller rollback faktiskt kommer att göra det, eller om de är i ett annat lager av kapsling.
(Kan du säga att jag har haft den här diskussionen några gånger? :-)
Uppdatering 1: Propel är ett PHP-databasåtkomstbibliotek som stöder konceptet med den "inre transaktionen" som inte binder sig när du säger till den. Att börja en transaktion ökar bara en räknare, och commit/rollback minskar räknaren. Nedan är ett utdrag från en e-postlisttråd där jag beskriver några scenarier där det misslyckas.
Uppdatering 2: Doktrin DBAL har också denna funktion. De kallar det Transaction Nesting.
Om du gillar det eller inte, transaktioner är "globala" och de följer inte objektorienterad inkapsling.
Problemscenario 1
Jag anropar commit()
, är mina ändringar genomförda? Om jag kör i en "inre transaktion" är de inte det. Koden som hanterar den yttre transaktionen skulle kunna välja att återställa, och mina ändringar skulle kasseras utan min vetskap eller kontroll.
Till exempel:
- Modell A:påbörja transaktionen
- Modell A:utför några ändringar
- Modell B:påbörja transaktion (tyst no-op)
- Modell B:exekvera några ändringar
- Modell B:commit (tyst no-op)
- Modell A:återställning (kasserar både modell A-ändringar och modell B-ändringar)
- Modell B:WTF!? Vad hände med mina ändringar?
Problemscenario 2
En inre transaktion rullar tillbaka, den kan kassera legitima ändringar gjorda av en yttre transaktion. När kontrollen återförs till den yttre koden, tror den att dess transaktion fortfarande är aktiv och tillgänglig för att utföras. Med din patch kan de anropa commit()
, och eftersom transDepth nu är 0, skulle det tyst ställa in $transDepth
till -1 och returnera sant, efter att du inte har begått något.
Problemscenario 3
Om jag anropar commit()
eller rollback()
när det inte finns någon aktiv transaktion ställer den in $transDepth
till -1. Nästa beginTransaction()
ökar nivån till 0, vilket innebär att transaktionen varken kan återställas eller genomföras. Efterföljande anrop till commit()
kommer bara att minska transaktionen till -1 eller längre, och du kommer aldrig att kunna förbinda dig förrän du gör en annan överflödig beginTransaction()
för att öka nivån igen.
I grund och botten är det en dömd idé att försöka hantera transaktioner i applikationslogik utan att tillåta databasen att sköta bokföringen. Om du har ett krav på två modeller för att använda explicit transaktionskontroll i en applikationsbegäran, måste du öppna två DB-anslutningar, en för varje modell. Sedan kan varje modell ha sin egen aktiva transaktion, som kan utföras eller återställas oberoende av varandra.