我查看了rollBack()
,commit()
和various transaction stuff的资源,但我无法确定rollBack()
之后是否可以调用commit()
已被召唤。
情况如下:
我有两个不同的数据库:$dbu = new PDO(..db1..)
和$dbb = new PDO(..db2..)
两个数据库都有在单个函数中更新的表。操作是 all或none - 所有表都已成功更新,或者都没有。
使用两个单独的事务,如果$dbu
的事务成功完成,但$dbb
的事务失败,我必须撤消在第一个事务中执行的操作:
代码块
$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();
}
由于commit()
的{{1}}在整个$dbu
交易之后被称为,因此可能会出现$dbb
成功的情况,{{ 1}}失败了。如果发生这种情况,我需要撤消$dbb
交易中的操作。
因此...
我可以拨打$dbu
(代码块2 的末尾) AFTER $dbb
已经运行了吗?或者我是否陷入了与最初相同的情况,我必须手动撤消$dbb->rollBack();
交易中发生的任何事情?同样,这不是理想的。如果连接在此中间丢失,我可能会在$dbb->commit();
表中留下不应该存在的数据(因为$dbb
事务失败)。
也许我可以将两个事务合并为一个$dbb
块?
代码块
$dbu
但这与代码块2 看起来并不完全不同,因为我们仍然可能遇到try/catch
成功但$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();
}
失败的情况。如果发生这种情况,那么我们仍在尝试在合作伙伴提交处理后调用$dbu->commit();
。
如果 无法在$dbb->commit();
之后调用$dbu->rollBack();
,是否有一种常用方法来解决这个2-DB问题?与rollBack()
一样有效的东西,并不需要整个额外的事务来撤销前一个操作。
编辑1
添加到代码块3 ,我可以在调用时验证每次执行吗?
代码块4
commit()
这有助于确保两个rollBack()
语句获得成功的可能性最大吗?或者$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
相反。
答案 0 :(得分:3)
跨不同的数据库连接进行正确的事务是不可能的。
虽然你可以做一些尴尬的解决方法,但它不会让你的交易成为真正的交易。
因此,您必须将所有操作保留在单个数据库中或忘记事务
答案 1 :(得分:2)
您无法回滚已提交的更改。
与您的other question代码块3一样,是可行的方法。即使提交可能失败,它也不会因为常见错误(例如错误的语法或约束违规或其他原因)而失败。假设整个PHP进程可能在两次提交之间被杀死,重置后者让你没有机会修复代码中产生的错误。但是,您必须单独处理这些罕见的例外(例如备份),因为我没有看到在代码中处理它们的有效方法。
还要记住,提交更改时已经应用但尚未“已发布”。因此提交本身很少会失败(仅出于特殊原因)。
@EDIT 1
处理错误的方式取决于您设置PDO实例的方式。请参阅documentation on how errors can be handled by PDO。
如果您使用默认模式(未明确设置错误模式或将其设置为PDO::ERRMODE_SILENT
),您的代码块4将起作用。