【发布时间】:2016-09-18 16:57:15
【问题描述】:
我查看了rollBack()、commit() 和various transaction stuff 的资源,但我找不到在commit() 已经被调用之后是否可以调用rollBack()。
情况是这样的:
我有两个不同的数据库:$dbu = new PDO(..db1..) 和 $dbb = new PDO(..db2..)
两个数据库都有在一个函数中更新的表。操作是 all or none - 要么所有表都成功更新,要么没有。
使用两个单独的事务,如果$dbu 的事务成功完成,但$dbb 的事务失败,我必须撤消第一个事务中所做的操作:
代码块 1
$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();
}
由于$dbu 的commit() 在整个$dbb 事务之后被调用,因此可能会出现$dbb 成功而$dbu 失败的情况。如果发生这种情况,我需要撤消在 $dbb 事务中所做的操作。
那么...
我可以打电话给$dbb->rollBack();(接近代码块2的末尾)之后$dbb->commit();已经运行了吗?还是我陷入了与最初相同的情况,我必须手动撤销$dbb 事务中发生的任何事情?同样,这并不理想。如果连接在此过程中断开,我可能会在 $dbb 表中留下不应该存在的数据(因为 $dbu 事务失败)。
也许我可以将两个交易合并成一个try/catch 块?
代码块 3
$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();
}
但这看起来与 代码块 2 并没有太大的不同,因为我们仍然可以遇到 $dbu->commit(); 成功但 $dbb->commit(); 失败的情况。如果发生这种情况,那么在其合作伙伴提交已被处理后,我们仍会尝试调用 $dbu->rollBack();。
如果我不能在commit() 之后调用rollBack(),有没有常用的方法来解决这个2-DB 问题?与rollBack() 一样高效且不需要整个额外事务来撤消前一个操作的东西。
编辑 1
添加到 代码块 3,我可以在调用每个执行时验证它们吗?
代码块 4
$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 块是否会在调用自定义块之前自动抛出PDOException?最好有一个简单的标识符来知道哪个事务失败,而不是整个$e->getMessage();。
【问题讨论】:
标签: php mysql pdo transactions