首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >嵌套事务的总回滚

嵌套事务的总回滚
EN

Stack Overflow用户
提问于 2013-06-12 09:53:20
回答 2查看 2.5K关注 0票数 2

我非常喜欢Yii的这个NestedPDO解决方案,但是我需要一些不同的事务处理。

只有当所有嵌套事务都可以执行,并且如果一个事务执行回滚时,所有事务都应该回滚,我才想提交嵌套事务。

我怎么能这么做?

我试图更改没有工作的rollBack函数:

代码语言:javascript
复制
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,这应该意味着自动提交是on的,但是在事务中,自动提交应该设置为0。我不确定这是否是整个回滚对我不起作用的原因?

EN

回答 2

Stack Overflow用户

发布于 2019-01-09 15:12:34

基于@RandomSeed的回答,我为默认的Yii事务处理创建了一个“下拉列表”:

代码语言:javascript
复制
$connection = Yii::app()->db;
$transaction=$connection->beginTransaction();
try
{
   $connection->createCommand($sql1)->execute();
   $connection->createCommand($sql2)->execute();
   //.... other SQL executions
   $transaction->commit();
}
catch(Exception $e)
{
   $transaction->rollback();
}

这是我的SingleTransactionManager课程:

代码语言:javascript
复制
class SingleTransactionManager extends CComponent 
{
    // The current transaction level.
    private $transLevel = 0;

    // The CDbConnection object that should be wrapped
    public $dbConnection;

    public function init()
    {
        if($this->dbConnection===null)
            throw new Exception('Property `dbConnection` must be set.');

        $this->dbConnection=$this->evaluateExpression($this->dbConnection);
    }
    // We only start a transaction if we're the first doing so
    public function beginTransaction() {
        if($this->transLevel == 0) {
            $transaction = parent::beginTransaction();
        } else {
            $transaction = new SingleTransactionManager_Transaction($this->dbConnection, false);
        }
        // always increase transaction level:
        $this->transLevel++;

        return $transaction;
    }

    public function __call($name, $parameters)
    {
        return call_user_func_array(array($this->dbConnection, $name), $parameters);
    }
}

class SingleTransactionManager_Transaction extends CDbTransaction
{
    // boolean, whether this instance 'really' started the transaction
    private $_startedTransaction;

    public function __construct(CDbConnection $connection, $startedTransaction = false)
    {
        $this->_startedTransaction = $startedTransaction;
        parent::__construct($connection);
        $this->setActive($startedTransaction);
    }

    // We only commit a transaction if we've started the transaction
    public function commit() {
        if($this->_startedTransaction)
            parent::commit();
    }

    // We only rollback a transaction if we've started the transaction
    // else throw an Exception to revert parent transactions/take adquate action
    public function rollback() {
        if($this->_startedTransaction)
            parent::rollback();
        else
            throw new Exception('Child transaction rolled back!');
    }
}

这个类“包装”主数据库连接,您应该在您的配置中将它声明为如下组件:

代码语言:javascript
复制
'components'=>array(

    // database
    'db'=>array(
        'class' => 'CDbConnection',
        // using mysql
        'connectionString'=>'....',
        'username'=>'...',
        'password'=>'....',
    ),

    // database
    'singleTransaction'=>array(
        'class' => 'pathToComponents.db.SingleTransactionManager',
        'dbConnection' => 'Yii::app()->db'
    )

注意,dbConnection属性应该是到主数据库连接的表达式。现在,当嵌套try catch块中的嵌套事务时,您可以在例如嵌套事务3中创建一个错误,并且还可以回滚1和2上的事务。

测试代码:

代码语言:javascript
复制
$connection = Yii::app()->singleTransaction;

$connection->createCommand('CREATE TABLE IF NOT EXISTS `test_transactions` (
  `number` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;')->execute();

$connection->createCommand('TRUNCATE TABLE `test_transactions`;')->execute();

testNesting(4, 3, 1);

echo '<br>';
echo 'Rows:';
echo '<br>';
$rows = $connection->createCommand('SELECT * FROM `test_transactions`')->queryAll();
if($rows)
{
    foreach($rows as $row)
    {
        print_r($row);
    }
}
else
    echo 'Table is empty!';

function testNesting(int $total, int $createErrorIn = null, int $current = 1)
{
    if($current>=$total)
        return;

    $connection = Yii::app()->singleTransaction;
    $indent = str_repeat('&nbsp;', ($current*4));

    echo $indent.'Transaction '.$current;
    echo '<br>';
    $transaction=$connection->beginTransaction();
    try
    {
        // create nonexisting columnname when we need to create an error in this nested transaction
        $columnname = 'number'.($createErrorIn===$current ? 'rr' : '');
        $connection->createCommand('INSERT INTO `test_transactions` (`'.$columnname.'`) VALUES ('.$current.')')->execute();

        testNesting($total, $createErrorIn, ($current+1));

        $transaction->commit();
    }
    catch(Exception $e)
    {
        echo $indent.'Exception';
        echo '<br>';
        echo $indent.$e->getMessage();
        echo '<br>';
        $transaction->rollback();
    }
}

下列产出的结果:

代码语言:javascript
复制
    Transaction 1
        Transaction 2
            Transaction 3
            Exception
            CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'numberrr' in 'field list'. The SQL statement executed was: INSERT INTO `test_transactions` (`numberrr`) VALUES (3)
        Exception
        Child transaction rolled back!
    Exception
    Child transaction rolled back!

Rows:
Table is empty! 
票数 0
EN

Stack Overflow用户

发布于 2019-01-09 15:22:07

IMHO,在应用程序代码中模拟“嵌套事务”的想法是反模式的。在应用程序中有许多不可能解决的异常情况(请参阅我对https://stackoverflow.com/a/319939/20860的回答)。

在PHP中,最好保持简单。工作自然地被组织成请求,因此使用请求作为事务范围。

  • 在调用任何模型类之前,在控制器级别启动事务。
  • 如果有什么问题,让模型抛出异常。
  • 在控制器级别捕获异常,必要时回滚。
  • 如果没有例外情况,请提交。

忘记所有关于事务级别的废话吧。模型不应该启动、提交或回滚任何事务。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17062538

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档