Het raamwerk kan niet weten of u een transactie bent begonnen. U kunt zelfs $db->query('START TRANSACTION')
. gebruiken waarvan het raamwerk niets zou weten omdat het geen SQL-instructies die u uitvoert ontleedt.
Het punt is dat het een applicatieverantwoordelijkheid is om bij te houden of je een transactie hebt gestart of niet. Het is niet iets dat het raamwerk kan doen.
Ik weet dat sommige frameworks het proberen, en gekke dingen doen, zoals tellen hoe vaak je een transactie bent begonnen, en het alleen oplossen als je een gelijk aantal keren commit of rollback hebt gedaan. Maar dit is volkomen nep, omdat geen van je functies kan weten of commit of rollback het echt zal doen, of dat ze zich in een andere nestlaag bevinden.
(Kun je zien dat ik deze discussie al een paar keer heb gehad? :-)
Update 1: Propel is een PHP-databasetoegangsbibliotheek die het concept van de "innerlijke transactie" ondersteunt die niet vastlegt wanneer u dit opdraagt. Het starten van een transactie verhoogt alleen een teller, en commit/rollback verlaagt de teller. Hieronder is een fragment uit een mailinglijstthread waarin ik een paar scenario's beschrijf waarin het niet lukt.
Update 2: Doctrine DBAL heeft deze functie ook. Ze noemen het Transaction Nesting.
Of je het nu leuk vindt of niet, transacties zijn 'wereldwijd' en ze gehoorzamen niet aan objectgeoriënteerde inkapseling.
Probleemscenario #1
Ik bel commit()
, zijn mijn wijzigingen vastgelegd? Als ik een "innerlijke transactie" binnenloop, zijn ze dat niet. De code die de buitenste transactie beheert, zou ervoor kunnen kiezen om terug te draaien, en mijn wijzigingen zouden worden weggegooid zonder mijn medeweten of controle.
Bijvoorbeeld:
- Model A:begin transactie
- Model A:voer enkele wijzigingen uit
- Model B:begin transactie (stille no-op)
- Model B:voer enkele wijzigingen uit
- Model B:commit (stille no-op)
- Model A:terugdraaien (verwerpt zowel wijzigingen in model A als wijzigingen in model B)
- Model B:WTF!? Wat is er met mijn wijzigingen gebeurd?
Probleemscenario #2
Een innerlijke transactie wordt teruggedraaid, het kan legitieme wijzigingen die door een uiterlijke transactie zijn aangebracht, negeren. Wanneer de controle wordt teruggegeven aan de buitenste code, gelooft deze dat de transactie nog steeds actief is en beschikbaar is om te worden vastgelegd. Met jouw patch kunnen ze commit()
. aanroepen , en aangezien de transDepth nu 0 is, zou het stil $transDepth
instellen naar -1 en true teruggeven, nadat je niets hebt vastgelegd.
Probleemscenario #3
Als ik commit()
. aanroep of rollback()
wanneer er geen transactie actief is, stelt het de $transDepth
. in naar -1. De volgende beginTransaction()
verhoogt het niveau naar 0, wat betekent dat de transactie niet kan worden teruggedraaid of vastgelegd. Daaropvolgende aanroepen van commit()
zal de transactie gewoon verlagen naar -1 of verder, en je zult nooit in staat zijn om vast te leggen totdat je nog een overbodige beginTransaction()
doet om het niveau opnieuw te verhogen.
Kortom, proberen om transacties in applicatielogica te beheren zonder de database de boekhouding te laten doen, is een gedoemd idee. Als u twee modellen nodig hebt om expliciet transactiebeheer in één toepassingsverzoek te gebruiken, moet u twee DB-verbindingen openen, één voor elk model. Dan kan elk model zijn eigen actieve transactie hebben, die onafhankelijk van elkaar kan worden vastgelegd of teruggedraaid.