この記事では主に PHP で MySQL のネストされたトランザクションを実装するための 2 つのソリューションを紹介します。この記事では doctrine と laravel の実装方法を分析し、分析と要約のために抽出します
1. 問題の原因
MySQL の公式ドキュメントには、ネストされたトランザクションはサポートされていないという明確な記述があります:
Transactions cannot be nested. This is a consequence of the implicit commit performed for any current transaction when you issue a START TRANSACTION statement or one of its synonyms.
しかし、複雑なシステムを開発する場合、これは避けられません。たとえば、関数 A が関数 B を呼び出し、関数 A がトランザクションを使用し、関数 B にもトランザクションが存在するため、トランザクションのネストが発生することがあります。この時点では、A の事柄は実際にはほとんど重要ではありません。なぜですか。上記のドキュメントで言及されており、簡単に翻訳すると次のようになります。
当执行一个START TRANSACTION指令时,会隐式的执行一个commit操作。
したがって、システム アーキテクチャ レベルでトランザクションのネストをサポートする必要があります。幸いなことに、Doctrine や Laravel など、一部の成熟した ORM フレームワークではネストがサポートされています。次に、これら 2 つのフレームワークがどのように実装されているかを見てみましょう。
注意してください、これら 2 つのフレームワークにおける関数と変数の名前付けは比較的直感的ですが、非常に長く見えますが、名前付けを通じて関数や変数の意味を直接知ることができるので、心配する必要はありません。このような大きな混乱を見たとき:)
2. Doctrine の解決策
まず、Doctrine でトランザクションを作成するコードを見てみましょう (関連のないコードは強制終了されました) :
public function beginTransaction() { ++$this->_transactionNestingLevel; if ($this->_transactionNestingLevel == 1) { $this->_conn->beginTransaction(); } else if ($this->_nestTransactionsWithSavepoints) { $this->createSavepoint($this->_getNestedTransactionSavePointName()); } }
この関数の最初の行は、_transactionNestingLevel を使用して現在のネスト レベルを識別し、それが 1 の場合、つまりネストがまだない場合は、デフォルトのメソッドを使用して START TRANSACTION を実行します。これが 1 より大きい場合、つまりネストがある場合、このセーブポイントは、ロールバックが必要な場合にのみ、このポイントまでロールバックできるトランザクション記録ポイントとして理解できます。
次に、rollBack 関数を見てください:
public function rollBack() { if ($this->_transactionNestingLevel == 0) { throw ConnectionException::noActiveTransaction(); } if ($this->_transactionNestingLevel == 1) { $this->_transactionNestingLevel = 0; $this->_conn->rollback(); $this->_isRollbackOnly = false; } else if ($this->_nestTransactionsWithSavepoints) { $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); --$this->_transactionNestingLevel; } else { $this->_isRollbackOnly = true; --$this->_transactionNestingLevel; } }
レベルが 1 の場合は直接ロールバックし、それ以外の場合は前のセーブポイントにロールバックする処理方法も非常に単純であることがわかります。
それではコミット関数を見ていきましょう:
public function commit() { if ($this->_transactionNestingLevel == 0) { throw ConnectionException::noActiveTransaction(); } if ($this->_isRollbackOnly) { throw ConnectionException::commitFailedRollbackOnly(); } if ($this->_transactionNestingLevel == 1) { $this->_conn->commit(); } else if ($this->_nestTransactionsWithSavepoints) { $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); } --$this->_transactionNestingLevel; }
忘れてください。この段落については簡単に説明しましょう:)
3. Laravel の解決策
Laravel の処理方法は比較的シンプルで大雑把ですが、まずトランザクションを作成する操作を見てみましょう。とても簡単ですよね?まず、現在存在するトランザクションの数を確認します。それが最初のトランザクションである場合、トランザクションは開始されます。それ以外の場合、何も行われないのはなぜでしょうか。引き続き rollBack の操作を見てみましょう:
public function beginTransaction() { ++$this->transactions; if ($this->transactions == 1) { $this->pdo->beginTransaction(); } }
理解できましたか?現在のトランザクションが 1 つだけである場合にのみ、実際にロールバックされます。それ以外の場合は、カウントが 1 つだけデクリメントされます。これが、Laravel の処理が比較的単純で粗雑であると述べた理由です。実際には、ネストされた内側の層には実際のトランザクションは存在しません。最も外側の層には全体的なトランザクションがあるだけです。しかし、これによって問題も解決されます。内部層が新しいトランザクションを作成すると、コミットの問題が発生します。原則は次のとおりです。完全を期すために、コミット コードもコピーしてください。
public function rollBack() { if ($this->transactions == 1) { $this->transactions = 0; $this->pdo->rollBack(); } else { --$this->transactions; } }
以上がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。
関連する推奨事項:
以上がPHP で MySQL のネストされたトランザクションを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。