フレームワークには、トランザクションを開始したかどうかを知る方法がありません。 $db->query('START TRANSACTION')
を使用することもできます 実行するSQLステートメントを解析しないため、フレームワークはこれを認識しません。
重要なのは、トランザクションを開始したかどうかを追跡するのはアプリケーションの責任であるということです。フレームワークでできることではありません。
一部のフレームワークがそれを実行しようとしていることを知っています。トランザクションを開始した回数をカウントし、一致する回数のコミットまたはロールバックを実行した場合にのみ解決するなどのコカマミーを実行します。しかし、これは完全に偽物です。コミットまたはロールバックが実際にそれを実行するかどうか、またはネストの別のレイヤーにあるかどうかを関数が認識できないためです。
(私がこの議論を数回行ったことがあると言えますか?:-)
更新1: Propel は、指示されたときにコミットしない「内部トランザクション」の概念をサポートするPHPデータベースアクセスライブラリです。トランザクションを開始すると、カウンターがインクリメントされるだけで、コミット/ロールバックによってカウンターがデクリメントされます。以下は、失敗するいくつかのシナリオを説明するメーリングリストスレッドからの抜粋です。
更新2: Doctrine DBAL > この機能もあります。彼らはそれをトランザクションネスティングと呼んでいます。
好むと好まざるとにかかわらず、トランザクションは「グローバル」であり、オブジェクト指向のカプセル化には従いません。
問題シナリオ#1
commit()
を呼び出します 、私の変更はコミットされていますか? 「内部トランザクション」内で実行している場合、そうではありません。外部トランザクションを管理するコードはロールバックを選択する可能性があり、私の変更は私の知識や制御なしに破棄されます。
例:
- モデルA:トランザクションを開始します
- モデルA:いくつかの変更を実行します
- モデルB:トランザクションを開始します(サイレントノーオペレーション)
- モデルB:いくつかの変更を実行します
- モデルB:コミット(サイレントノーオペレーション)
- モデルA:ロールバック(モデルAの変更とモデルBの変更の両方を破棄します)
- モデルB:WTF !?私の変更はどうなりましたか?
問題シナリオ#2
内部トランザクションはロールバックし、外部トランザクションによって行われた正当な変更を破棄する可能性があります。制御が外部コードに戻されると、そのトランザクションはまだアクティブであり、コミットできると考えられます。あなたのパッチで、彼らはcommit()
を呼び出すことができます 、transDepthが0になっているため、$transDepth
をサイレントに設定します。 何もコミットしなかった後、-1に設定してtrueを返します。
問題シナリオ#3
commit()
を呼び出すと またはrollback()
アクティブなトランザクションがない場合は、$transDepth
を設定します 〜-1。次のbeginTransaction()
レベルを0にインクリメントします。これは、トランザクションをロールバックしたりコミットしたりできないことを意味します。その後のcommit()
の呼び出し トランザクションを-1以上にデクリメントするだけで、別の不要なbeginTransaction()
を実行するまでコミットできません。 レベルを再度上げるには。
基本的に、データベースに簿記を許可せずにアプリケーションロジックでトランザクションを管理しようとすることは、運命のアイデアです。 1つのアプリケーションリクエストで明示的なトランザクション制御を使用するために2つのモデルが必要な場合は、モデルごとに1つずつ、2つのDB接続を開く必要があります。次に、各モデルに独自のアクティブなトランザクションを設定できます。このトランザクションは、互いに独立してコミットまたはロールバックできます。