# 执行事务

    当顺序地执行多个相关的语句时, 你或许需要将它们包在一个事务中来保证数据库的完整性和一致性。 如果这些语句中的任何一个失败了, 数据库将回滚到这些语句执行前的状态。

    下面的代码展示了一个使用事务的典型方法:
Yii::$app->db->transaction(function($db) {
    $db->createCommand($sql1)->execute();
    $db->createCommand($sql2)->execute();
    // ... executing other SQL statements ...
});

上述代码等价于下面的代码,但是下面的代码给予了你对于错误处理代码的更多掌控:

$db = Yii::$app->db;
$transaction = $db->beginTransaction();
try {
    $db->createCommand($sql1)->execute();
    $db->createCommand($sql2)->execute();
    // ... executing other SQL statements ...
    
    $transaction->commit();
} catch(\Exception $e) {
    $transaction->rollBack();
    throw $e;
} catch(\Throwable $e) {
    $transaction->rollBack();
    throw $e;
}

通过调用 beginTransaction() 方法, 一个新事务开始了。 事务被表示为一个存储在 $transaction 变量中的 yii\db\Transaction 对象。 然后,被执行的语句都被包含在一个 try...catch... 块中。 如果所有的语句都被成功地执行了, commit() 将被调用来提交这个事务。 否则, 如果异常被触发并被捕获, rollBack() 方法将被调用, 来回滚事务中失败语句之前所有语句所造成的改变。 throw $e 将重新抛出该异常, 就好像我们没有捕获它一样, 因此正常的错误处理程序将处理它。

# 嵌套事务

如果你的数据库支持保存点,你可以像下面这样嵌套多个事务:

Yii::$app->db->transaction(function ($db) {
    // outer transaction
    
    $db->transaction(function ($db) {
        // inner transaction
    });
});

或者,

$db = Yii::$app->db;
$outerTransaction = $db->beginTransaction();
try {
    $db->createCommand($sql1)->execute();

    $innerTransaction = $db->beginTransaction();
    try {
        $db->createCommand($sql2)->execute();
        $innerTransaction->commit();
    } catch (\Exception $e) {
        $innerTransaction->rollBack();
        throw $e;
    } catch (\Throwable $e) {
        $innerTransaction->rollBack();
        throw $e;
    }

    $outerTransaction->commit();
} catch (\Exception $e) {
    $outerTransaction->rollBack();
    throw $e;
} catch (\Throwable $e) {
    $outerTransaction->rollBack();
    throw $e;
}
编辑当前页 (opens new window)