如何检测交易已启动?

2022-08-30 14:56:28

我正在使用Zend_Db在事务中插入一些数据。我的函数启动一个事务,然后调用另一个方法,该方法也尝试启动事务,当然失败了(我正在使用MySQL5)。那么,问题是 - 我如何检测交易已经启动?下面是一段代码示例:

       try {
                    Zend_Registry::get('database')->beginTransaction();

                    $totals = self::calculateTotals($Cart);
                    $PaymentInstrument = new PaymentInstrument;
                    $PaymentInstrument->create();
                    $PaymentInstrument->validate();
                    $PaymentInstrument->save();

                    Zend_Registry::get('database')->commit();
                    return true;

            } catch(Zend_Exception $e) {
                    Bootstrap::$Log->err($e->getMessage());
                    Zend_Registry::get('database')->rollBack();
                    return false;
            }

在 PaymentInstrument::create 内部,还有另一个 beginTransaction 语句,该语句生成异常,指出交易已启动。


答案 1

框架无法知道您是否启动了事务。您甚至可以使用框架不知道的内容,因为它不会解析您执行的SQL语句。$db->query('START TRANSACTION')

关键是,跟踪您是否已启动事务是应用程序的责任。这不是框架可以做的事情。

我知道有些框架会尝试这样做,并做一些令人讨厌的事情,比如计算你开始一个事务的次数,只有在你完成提交或回滚匹配的次数时才解决它。但这完全是假的,因为你的函数都不知道提交或回滚是否真的会这样做,或者它们是否在另一层嵌套中。

(你能看出我已经讨论过几次了吗?:-)

更新1:Propel是一个PHP数据库访问库,它支持“内部事务”的概念,当你告诉它时不会提交。开始事务只会递增计数器,而提交/回滚会递减计数器。以下是邮件列表线程的摘录,我在其中描述了一些失败的情况。

更新2:Doctrine DBAL也具有此功能。他们称之为事务嵌套。


无论喜欢与否,事务都是“全局的”,它们不服从面向对象的封装。

问题场景 #1

我致电 ,我的更改是否已提交?如果我在“内部事务”中运行,它们不是。管理外部事务的代码可以选择回滚,我的更改将在我不知情或不受控制的情况下被丢弃。commit()

例如:

  1. 模式A:开始交易
  2. 模型A:执行一些更改
  3. 模式B:开始交易(静默无操作)
  4. 模型 B:执行一些更改
  5. 模型 B:提交(静默无操作)
  6. 模型 A:回滚(放弃模型 A 更改和模型 B 更改)
  7. B型:跆拳道!?我的更改发生了什么变化?

问题场景 #2

内部事务回滚,它可能会丢弃外部事务所做的合法更改。当控制权返回到外部代码时,它认为其事务仍处于活动状态,可以提交。使用你的补丁,他们可以调用 ,并且由于 transDepth 现在是 0,它会静默地设置为 -1 并在不提交任何内容后返回 true。commit()$transDepth

问题场景 #3

如果我调用或当没有活动的事务时,它会将设置为 -1。下一个将级别递增到 0,这意味着事务既不能回滚也不能提交。随后调用 to 只会将事务递减到 -1 或更远,并且您永远无法提交,直到您执行另一个多余的操作来再次递增级别。commit()rollback()$transDepthbeginTransaction()commit()beginTransaction()

基本上,尝试在应用程序逻辑中管理事务而不允许数据库进行簿记是一个注定要失败的想法。如果需要两个模型在一个应用程序请求中使用显式事务控制,则必须打开两个数据库连接,每个模型一个。然后,每个模型都可以有自己的活动事务,这些事务可以彼此独立地提交或回滚。


答案 2

做一个 try/catch:如果异常是事务已经启动(基于错误代码或字符串的消息,等等),请继续。否则,请再次引发异常。


推荐