PHP,MySQL,PDO事务 - 调用commit()后可以使用rollBack()吗?

时间:2016-05-21 13:13:04

标签: php mysql pdo transactions

我查看了rollBack()commit()various transaction stuff的资源,但我无法确定rollBack()之后是否可以调用commit()已被召唤。

情况如下:

我有两个不同的数据库:$dbu = new PDO(..db1..)$dbb = new PDO(..db2..)

两个数据库都有在单个函数中更新的表。操作是 all或none - 所有表都已成功更新,或者都没有。

使用两个单独的事务,如果$dbu的事务成功完成,但$dbb的事务失败,我必须撤消在第一个事务中执行的操作:

代码块

$dbu->beginTransaction();
try{
    $stmt = $dbu->prepare(...);
    $stmt->execute();

    // stuff

    $dbu->commit();
}catch(Exception $e){
    // do stuff

    $dbu->rollBack();
    exit();
}

$dbb->beginTransaction();
try{
    $stmt = $dbb->prepare(...);
    $stmt->execute();

    // stuff

    $dbb->commit();
}catch(Exception $e){
    // do stuff

    $dbb->rollBack();

    // Need to undo what we did
    $dbu->beginTransaction();
    try{
        $stmt = $dbu->prepare(...);
        $stmt->execute();

        // opposite of whatever operation was in the first transaction

        $dbu->commit();
    }catch(Exception $e){
    }

    exit();
}

这很麻烦,如果两个主要交易之间的连接发生了某些事情,那就不可靠了。

所以我想做的是将第二个交易嵌套在第一个交易中。我能够做到这一点似乎是合乎逻辑的,因为$dbu$dbb是两个独特的PDO对象,指向两个独立的数据库。它看起来像:

代码块2

$dbu->beginTransaction();
try{
    $stmt = $dbu->prepare(...);
    $stmt->execute();

    // stuff

    $dbb->beginTransaction();
    try{
        $stmt = $dbb->prepare(...);
        $stmt->execute();

        // stuff

        $dbb->commit();
    }catch(Exception $e){
        // do stuff

        $dbb->rollBack();
        $dbu->rollBack(); // Since $dbu was first part of transaction, it needs to be rolled back too
        exit();
    }

    $dbu->commit();
}catch(Exception $e){
    // do stuff

    $dbu->rollBack();
    $dbb->rollBack(); // **THIS IS THE TRICKY LINE!**
    exit();
}

由于commit()的{​​{1}}在整个$dbu交易之后被称为,因此可能会出现$dbb成功的情况,{{ 1}}失败了。如果发生这种情况,我需要撤消$dbb交易中的操作。

因此...

我可以拨打$dbu代码块2 的末尾) AFTER $dbb已经运行了吗?或者我是否陷入了与最初相同的情况,我必须手动撤消$dbb->rollBack();交易中发生的任何事情?同样,这不是理想的。如果连接在此中间丢失,我可能会在$dbb->commit();表中留下不应该存在的数据(因为$dbb事务失败)。

也许我可以将两个事务合并为一个$dbb块?

代码块

$dbu

但这与代码块2 看起来并不完全不同,因为我们仍然可能遇到try/catch成功但$dbu->beginTransaction(); $dbb->beginTransaction(); try{ $stmt = $dbu->prepare(...); $stmt->execute(); $stmt2 = $dbb->prepare(...); $stmt2->execute(); // stuff $dbu->commit(); $dbb->commit(); }catch(Exception $e){ // do stuff $dbu->rollBack(); $dbb->rollBack(); // **THIS IS THE TRICKY LINE!** exit(); } 失败的情况。如果发生这种情况,那么我们仍在尝试在合作伙伴提交处理后调用$dbu->commit();

如果 无法在$dbb->commit();之后调用$dbu->rollBack();,是否有一种常用方法来解决这个2-DB问题?与rollBack()一样有效的东西,并不需要整个额外的事务来撤销前一个操作。

编辑1

添加到代码块3 ,我可以在调用时验证每次执行吗?

代码块4

commit()

这有助于确保两个rollBack()语句获得成功的可能性最大吗?或者$dbu->beginTransaction(); $dbb->beginTransaction(); try{ $stmt = $dbu->prepare(...); if(!$stmt->execute()){ throw new Exeption('something somethign'); } $stmt2 = $dbb->prepare(...); if(!$stmt2->execute()){ throw new Exeption('something two'); } // stuff $dbu->commit(); $dbb->commit(); }catch(PDOException $e){ // do stuff $dbu->rollBack(); $dbb->rollBack(); // **THIS IS THE TRICKY LINE!** exit(); }catch(Exception $e){ // do stuff $dbu->rollBack(); $dbb->rollBack(); // **THIS IS THE TRICKY LINE!** exit(); } 块是否会在调用自定义块之前自动抛出commit?如果有一个简单的标识符可以知道哪个事务失败,那将是很好的,这与整个try/catch相反。

2 个答案:

答案 0 :(得分:3)

跨不同的数据库连接进行正确的事务是不可能的。

虽然你可以做一些尴尬的解决方法,但它不会让你的交易成为真正的交易。

因此,您必须将所有操作保留在单个数据库中或忘记事务

答案 1 :(得分:2)

您无法回滚已提交的更改。

与您的other question代码块3一样,是可行的方法。即使提交可能失败,它也不会因为常见错误(例如错误的语法或约束违规或其他原因)而失败。假设整个PHP进程可能在两次提交之间被杀死,重置后者让你没有机会修复代码中产生的错误。但是,您必须单独处理这些罕见的例外(例如备份),因为我没有看到在代码中处理它们的有效方法。

还要记住,提交更改时已经应用但尚未“已发布”。因此提交本身很少会失败(仅出于特殊原因)。

@EDIT 1

处理错误的方式取决于您设置PDO实例的方式。请参阅documentation on how errors can be handled by PDO

如果您使用默认模式(未明确设置错误模式或将其设置为PDO::ERRMODE_SILENT),您的代码块4将起作用。