我可以在commit()之前使用rollBack()吗?

时间:2016-06-05 12:53:55

标签: php mysql pdo transactions

这是我的剧本:

try{
    $db_conn->beginTransaction();

    $stm1 = $db_conn->prepare("UPDATE table1 SET col = 'updated' WHERE id = ?");
    $stm1->execute(array($value));
    $done = $stm->rowCount();

    if ($done){
        try {
            $stm2 = $db_conn->prepare("INSERT into table2 (col) VALUES (?)");
            $stm2->execute(array($id));

        } catch(PDOException $e){
            if ((int) $e->getCode() === 23000) {  // row is duplicate
                $stm3 = $db_conn->prepare("DELETE FROM table2 WHERE col = ?");
                $stm3->execute(array($id));

            } else {
                $db_conn->rollBack();    -- this
            }
        }

    } else {
        $error = true;
    }

    $db_conn->commit(); 
}
catch(PDOException $e){
    $db_conn->rollBack();
}

如您所见rollBack() 之前有commit()(我已-- this评论)。那是我正确的做法吗?或者rollBack()没用?

注意: DELETE语句用作撤消操作。假设你对一篇文章进行投票,你想要收回它。因此,如果您发送两次投票,DELETE语句会将其删除。

2 个答案:

答案 0 :(得分:2)

你不需要让它变得如此复杂。

您可以在单个try/catch内运行2个查询,因为任何具有isse的查询都会抛出异常,然后您可以执行单个回滚。

如果第一个查询失败,则不会更改数据库,回滚只会关闭事务。如果第二个查询失败,则回滚将取消第一个查询,即您之前执行的更新。

如果两个查询都完成OK,则提交会将更改应用于数据库。

try{
    $db_conn->beginTransaction();

    $stm1 = $db_conn->prepare("UPDATE table1 SET col = 'updated' WHERE id = ?");
    $stm1->execute(array($value));

    $stm2 = $db_conn->prepare("INSERT into table2 (col) VALUES (?)");
    $stm2->execute(array($id));

    $db_conn->commit(); 
}
catch(PDOException $e){
    $db_conn->rollBack();
}

其他说明

我看到你认为你想要做的事情。但是!

如果INSERT失败并显示23000错误代码,则INSERT将不会完成。您不必要的尝试删除失败的INSERT实际上会删除原来的行,即不应删除的原始投票!

答案 1 :(得分:1)

您的代码可以正常运行。如果要重新抛出错误(这将是“标准方式”),您可以摆脱内部回滚:

try {      
    ...
    if ($done){
        try {
            ...
        } catch(PDOException $e){
            if ((int) $e->getCode() === 23000) {  // row is duplicate
                ...
            } else {
                throw $e;                    
            }
        }
        ...
    } else {
        $error = true;
    }
    $db_conn->commit(); 
}
catch(PDOException $e){
   $db_conn->rollBack();
}    

在这种情况下,您的最终rollback将处理所有应该回滚的异常,同时自己处理一个例外23000而不回滚。

它也适用于您的代码。从技术上讲,您实际上可以根据需要组合尽可能多的commitsrollbacks,而不会导致错误 - 如果commit之后rollback,它将不会提交任何内容,因为它已经回滚了。如果您使用rollback而不使用start transaction(在自动提交模式下),它将不执行任何操作。维护代码和查看strcuture有点困难,这就是为什么你通常会使用上面的“标准方式”。

只需要考虑一件重要的事情:mysql中的事务不是嵌套的。如果您使用start transaction,则会在此之前自动执行commit。例如,

 start transaction;
 delete from very_important_table;
 start transaction;  -- will do an autocommit
 rollback;
 rollback;           -- will have no effect;
 commit;             -- will have no effect;
 rollback;           -- will have no effect; 

不会回滚您的very_import_table,因为第二个start transaction已经提交了它。