【问题标题】:Can I use rollBack() before commit()?我可以在 commit() 之前使用 rollBack() 吗?
【发布时间】:2016-10-05 03:49:18
【问题描述】:

这是我的脚本:

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();
}

如您所见,commit() 之前有一个rollBack()(我已经在 -- this 评论过)。那么我在做什么正确吗?还是说rollBack()没用?

注意:DELETE 语句用作撤消。假设您对某个帖子进行了投票,并且想要收回该帖子。因此,如果您发送两次投票,DELETE 语句将其删除。

【问题讨论】:

  • 回滚的全部意义在于,您无需使用删除手动反转插入(回滚为您完成了该操作,这就是“回滚”的意思)......也不需要嵌套 try/catch;只用外面的
  • @MarkBaker “只用外面的”,你在说什么? try/catchrollBack() ?
  • 我的意思是只使用一个在异常上回滚的 try/catch 块...摆脱 if ($done){ 内的那个 try/catch 块
  • @MarkBaker 好吧,我不能......!我必须使用嵌套的 try/catch 来确定重复的 ..!实际上我正在尝试这样做:更新 table1 然后从 table2 插入/删除 .. 插入或删除基于重复。
  • 是的,你可以。您已经通过外部尝试捕获了相同的异常。

标签: php mysql pdo transactions


【解决方案1】:

你不需要把它弄得那么复杂。

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

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

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

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 实际上会删除原来存在的行,即不应删除的原始投票!

【讨论】:

  • 你的代码和我的不一样..! DELETE 声明在哪里?
  • 您不需要删除,因为 ROLLBACK 会在运行时撤消更新和插入
  • delete 语句是完全没有必要的,这就是它不存在的原因......正如我在对您的问题的第一条评论中所说的那样
  • @MarkBaker 那我敢肯定你不知道我在做什么。
  • 好吧,态度没有帮助
【解决方案2】:

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

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 而不会导致错误 - 如果您在 rollback 之后使用 commit,它不会提交任何内容,因为它已经回滚了。如果您使用 rollback 而不使用 start transaction(在自动提交模式下),它将什么也不做。维护代码和查看结构有点困难,这就是为什么您通常会使用上面的“标准方式”。

只有一件重要的事情需要考虑: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 已经提交了它。

【讨论】:

  • 另外你的第二个else 应该替换为catch
  • @stack 其他属于你的if ($done){,我的代码中忘记了if,我会添加它。
猜你喜欢
  • 2015-10-15
  • 2016-09-18
  • 2011-05-14
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
  • 2016-10-04
  • 1970-01-01
  • 2020-09-30
相关资源
最近更新 更多