考虑以下情况:
(伪)代码:
$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
吗?答案 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
块的影响。