我正在使用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语句,它产生一个异常,说明该事务已经启动.
框架无法知道您是否开始了交易.您甚至可以使用$db->query('START TRANSACTION')
框架不知道的内容,因为它不会解析您执行的SQL语句.
关键是,跟踪您是否已开始交易是一项应用程序职责.这不是框架可以做的事情.
我知道有些框架会尝试这样做,并且做一些奇怪的事情,例如计算你开始事务的次数,只有当你完成提交或回滚匹配次数时才解决它.但这完全是假的,因为你的任何函数都不知道提交或回滚是否会实际执行它,或者它们是否在另一层嵌套中.
(你能告诉我这次讨论了几次吗?:-)
编辑: Propel是一个PHP数据库访问库,它支持"内部事务"的概念,当你告诉它时它不会提交.开始事务只会递增计数器,并且提交/回滚会递减计数器.下面是一个邮件列表线程的摘录,其中我描述了一些失败的场景.
无论喜欢与否,事务都是"全局的",并且它们不遵循面向对象的封装.
问题场景#1
我打电话commit()
,我的改变是否已承诺?如果我在"内部交易"中运行他们不是.管理外部事务的代码可以选择回滚,我的更改将在我不知情或无法控制的情况下被丢弃.
例如:
模型A:开始交易
模型A:执行一些更改
模型B:开始交易(无声无操作)
模型B:执行一些更改
模型B:提交(无声操作)
模型A:回滚(丢弃模型A更改和模型B更改)
模特B:WTF!?我的变化怎么了?
问题场景#2
内部事务回滚,它可以丢弃外部事务所做的合法更改.当控制权返回到外部代码时,它认为其事务仍处于活动状态且可以提交.使用你的补丁,他们可以调用commit()
,因为transDepth现在为0,所以$transDepth
在没有提交任何内容之后,它会默默地设置为-1并返回true.
问题场景#3
如果我调用commit()
或rollback()
没有活动的事务,它将设置$transDepth
为-1.下一步beginTransaction()
将级别增加到0,这意味着事务既不能回滚也不能提交.后续调用commit()
只会将事务减少到-1或更远,并且您将永远无法提交,直到您beginTransaction()
再次执行另一个多余的增量操作.
基本上,尝试在不允许数据库进行簿记的情况下管理应用程序逻辑中的事务是一个注定要失败的想法.如果要求两个模型在一个应用程序请求中使用显式事务控制,则必须打开两个数据库连接,每个模型一个.然后,每个模型都可以拥有自己的活动事务,可以相互独立地提交或回滚.
(见http://www.nabble.com/Zend-Framework-Db-Table-ORM-td19691776.html)