27


6

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

{Zend_Registry :: get( 'database') - > beginTransaction();を試してください。

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

Zend_Registry :: get( 'database') - > commit(); trueを返します。

catch(Zend_Exception $ e){Bootstrap :: $ Log-> err($ e-> getMessage());} Zend_Registry :: get( 'database') - > rollBack(); falseを返します。 }
PaymentInstrument

createの中には、トランザクションがすでに開始されていることを示す例外を生成するもう1つのbeginTransactionステートメントがあります。

11 Answer


25


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

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

私はいくつかのフレームワークがそれをやろうとしているのを知っています、そして、あなたがトランザクションを始めた回数を数えるのと同じくらい決心します。 しかし、コミットまたはロールバックが実際にそれを実行するのかどうか、またはそれらが別のネスト層にあるのかどうかを知ることができないため、これはまったくの偽りです。

(私はこの議論を数回行ったと言うことができますか? :-)

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

'' '' '

そうであろうとなかろうと、トランザクションは「グローバル」であり、それらはオブジェクト指向のカプセル化に従いません。

*問題シナリオ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にインクリメントします、つまりトランザクションはロールバックもコミットもできません。 その後の `commit()`の呼び出しはトランザクションを-1またはそれ以上にデクリメントするだけです、そしてもう一回余分な `beginTransaction()`を実行してレベルを上げるまでコミットすることはできません。

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

(http://www.nabble.com/Zend-Framework-Db-Table-ORM-td19691776.htmlを参照)


4


(エラーコードや文字列のメッセージに基づいて)トランザクションがすでに開始されていることが例外である場合は、続けてtry / catchを実行します。 それ以外の場合は、もう一度例外をスローしてください。


2


Zend_RegistryにbeginTransaction()の戻り値を格納して、後で確認してください。


2


Zend_Dbとアダプタ(mysqliとPDOの両方のバージョン)を見ると、トランザクションの状態をチェックするための良い方法は実際には見られません。 これに関してはhttp://framework.zend.com/issues/browse/ZF-870 [ZF問題]があるようです - 幸いなことに、近日中に公開予定のパッチもあります。

当分の間、非公式のZFコードを実行したくない場合は、http://www.php.net/manual/en/mysqli.autocommit.php [mysqliのドキュメント]に、「SELECT @@ autocommit」とすることができます。あなたが現在トランザクションに入っているかどうか調べてください(err …​ 自動コミットモードではありません。


1


次のようにコードを書くこともできます。

{Zend_Registry :: get( 'database') - > beginTransaction();を試してください。 catch(Exception $ e){}

{$ totals = self :: calculateTotals($ Cart);を試してください。

$ PaymentInstrument = new PaymentInstrument; $ PaymentInstrument-> create(); $ PaymentInstrument-> validate(); $ PaymentInstrument-> save();

Zend_Registry :: get( 'database') - > commit(); trueを返します。 catch(Zend_Exception $ e){Bootstrap :: $ Log-> err($ e-> getMessage());} Zend_Registry :: get( 'database') - > rollBack(); falseを返します。 }


1


innoDBの場合は、使用できるはずです

SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();


1


この議論はかなり古いです。 何人かが指摘したように、あなたはあなたのアプリケーションでそれをすることができます。 PHPにはバージョン5> = 5.3.3以降のメソッドがあり、トランザクションの途中にいるかどうかを知ることができます。 PDP

inTransaction()はtrueまたはfalseを返します。 リンクhttp://php.net/manual/en/pdo.intransaction.php


0


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

wrapper.php:

{//トランザクションインクルードの開始( "your_script.php")を試してください。 catch(RollbackException $ e){//トランザクションのロールバック}

あなたがいくつかの接続を開く可能性がある場合、状況は分割を使用することでもう少し複雑になります。 スクリプトの最後にトランザクションをコミットまたはロールバックする必要がある接続のリストにそれらを追加する必要があります。 ただし、シャーディングの場合、トランザクションにグローバルミューテックスがないと、コミット中に別のスクリプトがトランザクションをシャードにコミットする可能性があるため、同時トランザクションの真の分離またはアトミック性を容易に達成できないことに注意してください。あなたの しかし、MySQLの 分散トランザクションを調べてみてください。


0


Zendプロファイラを使用して、クエリテキストとしてbeginを、クエリタイプとしてZend_Db_Prfiler

TRANSACTIONを使用し、その後クエリテキストとしてout commitまたはrollbackを実行します。 (アプリケーションに - > query( "START TRANSACTION")とZend Profilerが有効になっていないと仮定して)


0


私はその言葉が好きですが、取引の開始を追跡することはcockamamieであるというBill Karwinの評価に同意しません。

私が書いていないモジュールから呼び出される可能性のあるイベントハンドラ関数がある状況があります。 私のイベントハンドラはデータベースにたくさんのレコードを作成します。 何かが正しく渡されなかったり、行方不明になったり、うまく行かなかったりしたら、私は間違いなくロールバックする必要があります。 コードが他の人によって書かれているので、イベントハンドラをトリガーする外部モジュールのコードがdbトランザクションを処理しているかどうかわかりません。 トランザクションが進行中かどうかを確認するためにデータベースに問い合わせる方法が見つかりませんでした。

だから私は数え続けます。 CodeIgniterを使っていますが、入れ子になったdbトランザクションの使用を開始するように依頼すると奇妙なことをしているようです(例: trans_start()メソッドを複数回呼び出します。 つまり、外部関数がtrans_start()も使用している場合、ロールバックとコミットが正しく行われないため、trans_start()をイベントハンドラに含めることはできません。 私はまだこれらの機能を正しく管理することを考え出していない可能性が常にありますが、私は多くのテストを実行しました。

私のすべてのイベントハンドラが知る必要があるのは、dbトランザクションが別のモジュールの呼び出しによって既に開始されているかどうかです。 もしそうであれば、それは別の新しいトランザクションを開始せず、いかなるロールバックも受け入れず、またコミットもしない。 何らかの外部機能がdbトランザクションを開始した場合、ロールバック/コミットも処理されることになります。

CodeIgniterのトランザクションメソッド用のラッパー関数があり、これらの関数はカウンタを増減します。

function transBegin(){//レベル数を増やす$ this  - > _ transBegin = 1; //深さが1レベルしかない場合は、次のようにトランザクションを作成できます。($ this  - > _ transBegin == 1){$ this-> db-> trans_begin(); }}

function transCommit(){if($ this  - > _ transBegin == 1){//深さが1レベルしかない場合は、トランザクションをコミットできます$ this-> db-> trans_commit(); // thisのレベル数をデクリメントする$ this  - > _ transBegin  -  = 1;

}

function transRollback(){if($ this  - > _ transBegin == 1){//深さが1レベルしかない場合は、トランザクション$ this-> db-> trans_rollback()をロールバックできます。 // thisのレベル数をデクリメントする$ this  - > _ transBegin  -  = 1; }

私の状況では、これが既存のdbトランザクションをチェックする唯一の方法です。 そしてそれはうまくいきます。 「アプリケーションがdbトランザクションを管理している」とは言いません。 それはこの状況では本当に間違っています。 アプリケーションの他の部分が何らかのdbトランザクションを開始したかどうかをチェックするだけなので、入れ子になったdbトランザクションが作成されるのを避けることができます。