在php异常上调用SQL回滚

时间:2015-03-06 18:42:22

标签: php sql mysqli

考虑以下情况:

  • 我使用mysqli扩展
  • 打开一个与MySQL的新连接
  • 我开始了一项交易
  • 我对MySQL数据库和一些php计算进行了一些查询
  • MySQL或php可能会抛出一些异常
  • 我提交了交易

(伪)代码:

$sql = new mysqli();
...
{ //some scope
    $sql->query("START TRANSACTION");
    ... 
    if (something)
        throw Exception("Something went wrong");
    ...
    $sql->query("COMMIT"); // or $sql->commit()
} //leave scope either normally or through exception
...
$sql->query( ... ); // other stuff not in transaction

如果以上所有内容都在某个大try catch块中(或者甚至可能未被捕获),则交易将保持未完成状态。如果我然后尝试在数据库上做更多的工作,那些操作将属于事务。

我的问题是:当我以异常方式离开创建交易的范围时,是否有一些可能的机制允许我自动发送$sql->rollback()


一些注意事项:

  • mysqli::autocommit与交易不同。它只是您打开或关闭自动提交的设置。相当于MySQL SET autocommit
  • mysqli::begin_transaction没有记录,我也不知道它的确切行为是什么。例如,当rollback对象死亡时,它会调用mysqli吗?

4 个答案:

答案 0 :(得分:1)

命令mysqli::begin_transaction$sql->query("START TRANSACTION");相同(面向对象的方式);

无法自动回滚异常。

如果一切都成功,你只能说。然后它将是一个"自动回滚"如果不。但是这样,如果你有多次提交,你很快就会遇到麻烦。

所以你现在的代码已经非常好了。我会以完整的OOP方式完成:

$sql->begin_transaction();
try {
   $sql->query('DO SOMETHING');
   if(!true) {
       throw new \Exception("Something went wrong");
   }
   $sql->commit();
}
catch (\Exception exception) {
   $sql->rollback();
}

您也可以编写自己的例外:

class SqlAutoRollbackException extends \Exception {
    function __construct($msg, Sql $sql) {
        $sql->rollback();
        parent::__construct($msg);
    }
}

但是你还需要抓住它。

答案 1 :(得分:0)

您可以使用DECLARE ... HANDLER语法处理mysql异常,但我觉得尝试通过PHP处理它们并不合适!

BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION  ROLLBACK;
        START TRANSACTION;
          INSERT INTO Users ( user_id ) VALUES('1');
          INSERT INTO Users ( user_id ) VALUES('2');
        COMMIT;
END;

可在此处找到更多信息: http://dev.mysql.com/doc/refman/5.5/en/declare-handler.html

答案 2 :(得分:0)

因此,如果出现问题,您希望提交事务或回滚事务,并且有很多方法可能发生问题 - 通过mysql错误或php抛出异常。那么为什么不在事务开始时设置一个标志,在事务准备好提交时设置该标志,并在完成该过程时检查标志的状态以查看是否需要回滚?

如果您害怕全局范围的变量,可以使用过于复杂的Singleton类或某种静态变量,但这是基本的想法:

function main() {
  global $commit_flag = false;
  start_transaction();
  doEverything();

  if ($commit_flag) {
    commit_transaction();
  } else {
    rollback_transaction();
  }
}

function doEverything() {
  global $commit_flag;
  try {
    /* do some stuff that may cause errors */

    $commit_flag = true;
  } catch { ... }
}

答案 3 :(得分:0)

您无法自动回滚异常,但只需一些代码就可以执行您想要的操作。即使您已经在try-catch区块中,也可以将它们嵌套到您的交易部分,例如:

try {
    $sql->query("START TRANSACTION");
    ... 
    if (something)
        throw new PossiblyCustomException("Something went wrong");
    ...
    $sql->query("COMMIT"); 
} catch (Exception $e) { //catch all exceptions
    $sql->query("ROLLBACK");
    throw $e; //rethrow the very same exception object
}

即使您使用最通用的catch Exception,也会知道异常的实际类型。当你重新抛出时,它仍然可以在PossiblyCustomException后被捕获。因此,您已拥有的所有处理仍然不受此新try-catch块的影响。