2

私はこの Yii 用のNestedPDOソリューションが本当に気に入っていますが、別のトランザクション処理が必要です。

ネストされたすべてのトランザクションをコミットできる場合にのみ、ネストされたトランザクションをコミットしたいと思います。1 つのトランザクションがロールバックする場合は、すべてのトランザクションをロールバックする必要があります。

どうやってやるの?

機能しなかったロールバック機能を変更しようとした私の試み:

public function rollBack() {
    $this->transLevel--;

    if($this->transLevel == 0 || !$this->nestable()) {
        parent::rollBack();
    } else {

        $level = $this->transLevel;
        for($level; $level>1; $level--){
            $this->exec("ROLLBACK TO SAVEPOINT LEVEL{$this->constantlevel}");
        }
        //parent::rollBack();
    }
}

私は NestedPDO を適応させることを考えていました: 関数 commit() では、最も外側のトランザクションでのみコミットを行い、関数 rollBack() では、どのサブトランザクションがロールバックを引き起こしたかに関係なく、最も外側のトランザクションにロールバックを行います。でもやりきれなかった…。

私は MySQL と InnoDB テーブルを使用していますが、自動コミットについてはよくわかりませんが、トランザクション内で自動コミットの値をエコーするとき、常に値 1 を取得します。これは、自動コミットがオンであることを意味しますが、トランザクション内では自動コミットを 0 に設定する必要があります。これがロールバック全体が機能しない原因かどうかわかりませんか?

4

3 に答える 3

0

エラーが発生するとすぐにトランザクション全体を自動的にロールバックしたい場合は、B特定の場所 (例: from A())から呼び出されたときに、 の例外ハンドラから例外を再スローすることができます。

function A(){
   ...
   $this->B(true);
   ...
}

/*
* @param B boolean Throw an exception if the transaction is rolled back
*/
function B($rethrow) {
    $transaction=Yii::app()->db->beginTransaction();
    try {
        //do something
        $transaction->commit();
    } catch(Exception $e) {
        $transaction->rollBack();
        if ($rethrow) throw $e;
    }
}

これで、トランザクションがすでに進行中かどうかをラッパーに検出させたいだけで、この場合はトランザクションを開始しないことを理解しました。

NestedPDOしたがって、クラスは本当に必要ありません。代わりに、次のようなクラスを作成できます。

class SingleTransactionManager extends PDO {
    private $nestingDepth = 0;

    public function beginTransaction() {
        if(!$this->nestingDepth++ == 0) {
            parent::beginTransaction();
        } // else do nothing
    }
    public function commit() {
        $this->nestingDepth--;
        if (--$this->nestingDepth == 0) {
            parent::commit();
        } // else do nothing
    }

    public function rollback() {
        parent::rollback();
        if (--$this->nestingDepth > 0) {
            $this->nestingDepth = 0;
            throw new Exception(); // so as to interrupt outer the transaction ASAP, which has become pointless
        }

    }
}
于 2013-06-12T11:44:57.123 に答える
0

私見、アプリケーションコードで「ネストされたトランザクション」をシミュレートするという考えはアンチパターンです。アプリケーションで解決できない異常なケースが多数あります ( https://stackoverflow.com/a/319939/20860に対する私の回答を参照してください)。

PHP では、シンプルに保つ方がよいでしょう。作業は自然にリクエストに編成されるため、リクエストをトランザクション スコープとして使用します。

  • モデル クラスを呼び出す前に、コントローラー レベルでトランザクションを開始します。
  • 何か問題が発生した場合、モデルに例外をスローさせます。
  • コントローラー レベルで例外をキャッチし、必要に応じてロールバックします。
  • 例外がキャッチされない場合はコミットします。

トランザクション レベルに関するナンセンスはすべて忘れてください。モデルは、トランザクションを開始、コミット、またはロールバックするべきではありません。

于 2019-01-09T15:22:07.183 に答える