30

Zend_Dbを使用して、トランザクション内にデータを挿入しています。私の関数はトランザクションを開始してから、トランザクションを開始しようとしてもちろん失敗する別のメソッドを呼び出します(MySQL5を使用しています)。したがって、問題は、トランザクションがすでに開始されていることをどのように検出するかです。コードのサンプルビットは次のとおりです。

       try {
                    Zend_Registry::get('database')->beginTransaction();

                    $totals = self::calculateTotals($Cart);
                    $PaymentInstrument = new PaymentInstrument;
                    $PaymentInstrument->create();
                    $PaymentInstrument->validate();
                    $PaymentInstrument->save();

                    Zend_Registry::get('database')->commit();
                    return true;

            } catch(Zend_Exception $e) {
                    Bootstrap::$Log->err($e->getMessage());
                    Zend_Registry::get('database')->rollBack();
                    return false;
            }

PaymentInstrument :: createの内部には、トランザクションがすでに開始されていることを示す例外を生成する別のbeginTransactionステートメントがあります。

4

11 に答える 11

29

フレームワークには、トランザクションを開始したかどうかを知る方法がありません。$db->query('START TRANSACTION')実行するSQLステートメントを解析しないため、フレームワークが認識しないものを使用することもできます。

重要なのは、トランザクションを開始したかどうかを追跡するのはアプリケーションの責任であるということです。フレームワークができることではありません。

一部のフレームワークがそれを実行しようとしていることを知っています。トランザクションを開始した回数をカウントし、一致する回数のコミットまたはロールバックを実行した場合にのみ解決するなどのコカマミーを実行します。しかし、これは完全に偽物です。コミットまたはロールバックが実際にそれを実行するかどうか、またはネストの別のレイヤーにあるかどうかを関数が認識できないためです。

(私がこの議論を数回行ったことがあると言えますか?:-)

アップデート1: Propelは、指示したときにコミットしない「内部トランザクション」の概念をサポートするPHPデータベースアクセスライブラリです。トランザクションを開始すると、カウンターがインクリメントされるだけで、コミット/ロールバックによってカウンターがデクリメントされます。以下は、それが失敗するいくつかのシナリオを説明するメーリングリストスレッドからの抜粋です。

アップデート2: DoctrineDBALにもこの機能があります。彼らはそれをトランザクションネスティングと呼んでいます。


好むと好まざるとにかかわらず、トランザクションは「グローバル」であり、オブジェクト指向のカプセル化には従いません。

問題シナリオ#1

私は電話commit()します、私の変更はコミットされていますか?「内部トランザクション」内で実行している場合、そうではありません。外部トランザクションを管理するコードはロールバックすることを選択でき、私の変更は私の知識や制御なしに破棄されます。

例えば:

  1. モデルA:トランザクションを開始します
  2. モデルA:いくつかの変更を実行します
  3. モデルB:トランザクションを開始します(サイレントノーオペレーション)
  4. モデルB:いくつかの変更を実行します
  5. モデルB:コミット(サイレントノーオペレーション)
  6. モデルA:ロールバック(モデルAの変更とモデルBの変更の両方を破棄します)
  7. モデルB:WTF !? 私の変更はどうなりましたか?

問題シナリオ#2

内部トランザクションはロールバックし、外部トランザクションによって行われた正当な変更を破棄する可能性があります。制御が外部コードに戻されると、そのトランザクションはまだアクティブであり、コミットできると考えられます。パッチを使用すると、彼らはを呼び出すことができcommit()、transDepthは0になっているため$transDepth、何もコミットしなかった後、サイレントに-1に設定され、trueを返します。

問題シナリオ#3

電話をかけるcommit()rollback()、アクティブなトランザクションがない場合は、$transDepthを-1に設定します。次beginTransaction()はレベルを0にインクリメントします。これは、トランザクションをロールバックしたりコミットしたりできないことを意味します。後続のへの呼び出しは、トランザクションを-1以上にデクリメントするだけであり、レベルを再度インクリメントするためにcommit()別の余分な操作を行うまで、コミットすることはできません。beginTransaction()

基本的に、データベースに簿記を許可せずにアプリケーションロジックでトランザクションを管理しようとすることは、運命のアイデアです。1つのアプリケーションリクエストで明示的なトランザクション制御を使用するために2つのモデルが必要な場合は、モデルごとに1つずつ、2つのDB接続を開く必要があります。次に、各モデルに独自のアクティブなトランザクションを設定できます。このトランザクションは、互いに独立してコミットまたはロールバックできます。

于 2008-11-26T06:53:03.043 に答える
4

try/catch を実行します。例外がトランザクションが既に開始されている場合 (エラー コードまたは文字列のメッセージなどに基づいて)、続行します。それ以外の場合は、例外を再度スローします。

于 2008-11-26T06:24:57.717 に答える
2

beginTransaction() の戻り値を Zend_Registry に格納し、後で確認します。

于 2008-11-26T05:25:52.127 に答える
2

Zend_Db とアダプター (mysqli と PDO の両方のバージョン) を見てみると、トランザクションの状態を確認する良い方法が見当たりません。これに関してはZF の問題があるようです。幸いなことに、まもなくパッチが公開される予定です。

とりあえず、非公式の ZF コードを実行したくない場合、mysqli のドキュメントには、現在トランザクション中であるかどうかを確認できると書かSELECT @@autocommitれています (エラー... 自動コミット モードではありません)。

于 2008-11-26T05:33:31.970 に答える
1

この議論はかなり古いものです。一部の人が指摘したように、アプリケーションでそれを行うことができます。PHP には、バージョン 5 >= 5.3.3 以降、トランザクションの最中かどうかを知るためのメソッドがあります。PDP::inTransaction() は true または false を返します。リンクhttp://php.net/manual/en/pdo.intransaction.php

于 2017-06-08T10:07:20.320 に答える
0

Web 向けの PHP では、ほとんどの場合、スクリプトは単一の Web 要求で呼び出されます。その場合に本当にやりたいことは、トランザクションを開始し、スクリプトが終了する直前にコミットすることです。何か問題が発生した場合は、例外をスローして全体をロールバックします。このような:

wrapper.php:

try {
   // start transaction
   include("your_script.php");
   // commit transaction
} catch (RollbackException $e) {
   // roll back transaction
}

複数の接続を開く可能性があるシャーディングの場合、状況はもう少し複雑になります。スクリプトの最後でトランザクションをコミットまたはロールバックする接続のリストにそれらを追加する必要があります。ただし、シャーディングの場合、トランザクションにグローバルミューテックスがない限り、並行トランザクションの真の分離または原子性を簡単に達成できないことに注意してください。コミット中に別のスクリプトがトランザクションをシャードにコミットしている可能性があるためです。あなたの。ただし、MySQL の分散トランザクションを確認することをお勧めします。

于 2013-07-15T01:50:05.717 に答える