一、问题起源
在MySQL的官方文档中有明确的说明不支持嵌套事务:
代码如下:
Transactionscannotbenested.ThisisaconsequenceoftheimplicitcommitperformedforanycurrenttransactionwhenyouissueaSTARTTRANSACTIONstatementoroneofitssynonyms.
但是在我们开发一个复杂的系统时难免会无意中在事务中嵌套了事务,比如A函数调用了B函数,A函数使用了事务,并且是在事务中调用了B函数,B函数也有一个事务,这样就出现了事务嵌套。这时候其实A的事务就意义不大了,为什么呢?上面的文档中就有提到,简单的翻译过来就是:
代码如下:
当执行一个STARTTRANSACTION指令时,会隐式的执行一个commit操作。
所以我们就要在系统架构层面来支持事务的嵌套。所幸的是在一些成熟的ORM框架中都做了对嵌套的支持,比如doctrine或者laravel。接下来我们就一起来看下这两个框架是怎样来实现的。
友情提示,这两个框架的函数和变量的命名都比较的直观,虽然看起来很长,但是都是通过命名就能直接得知这个函数或者变量的意思,所以不要一看到那么一大坨就被吓到了:)
二、doctrine的解决方案
首先来看下在doctrine中创建事务的代码(干掉了不相关的代码):
代码如下:
publicfunctionbeginTransaction()
{
++$this->_transactionNestingLevel;
if($this->_transactionNestingLevel==1){
$this->_conn->beginTransaction();
}elseif($this->_nestTransactionsWithSavepoints){
$this->createSavepoint($this->_getNestedTransactionSavePointName());
}
}
这个函数的第一行用一个_transactionNestingLevel来标识当前嵌套的级别,如果是1,也就是还没有嵌套,那就用默认的方法执行一下STARTTRANSACTION就ok了,如果大于1,也就是有嵌套的时候,她会帮我们创建一个savepoint,这个savepoint可以理解为一个事务记录点,当需要回滚时可以只回滚到这个点。
然后看下rollBack函数:
代码如下:
publicfunctionrollBack()
{
if($this->_transactionNestingLevel==0){
throwConnectionException::noActiveTransaction();
}
if($this->_transactionNestingLevel==1){
$this->_transactionNestingLevel=0;
$this->_conn->rollback();
$this->_isRollbackOnly=false;
}elseif($this->_nestTransactionsWithSavepoints){
$this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
–$this->_transactionNestingLevel;
}else{
$this->_isRollbackOnly=true;
–$this->_transactionNestingLevel;
}
}
可以看到处理的方式也很简单,如果level是1,直接rollback,否则就回滚到前面的savepoint。
然后我们继续看下commit函数:
代码如下:
publicfunctioncommit()
{
if($this->_transactionNestingLevel==0){
throwConnectionException::noActiveTransaction();
}
if($this->_isRollbackOnly){
throwConnectionException::commitFailedRollbackOnly();
}
if($this->_transactionNestingLevel==1){
$this->_conn->commit();
}elseif($this->_nestTransactionsWithSavepoints){
$this->releaseSavepoint($this->_getNestedTransactionSavePointName());
}
–$this->_transactionNestingLevel;
}
算了,不费口舌解释这段了吧:)
三、laravel的解决方案
laravel的处理方式相对简单粗暴一些,我们先来看下创建事务的操作:
代码如下:
publicfunctionbeginTransaction()
{
++$this->transactions;
if($this->transactions==1)
{
$this->pdo->beginTransaction();
}
}
感觉如何?soeasy吧?先判断当前有几个事务,如果是第一个,ok,事务开始,否则就啥都不做,那么为啥是啥都不做呢?继续往下看rollBack的操作:
代码如下:
publicfunctionrollBack()
{
if($this->transactions==1)
{
$this->transactions=0;
$this->pdo->rollBack();
}
else
{
–$this->transactions;
}
}
明白了吧?只有当当前事务只有一个的时候才会真正的rollback,否则只是将计数做减一操作。这也就是为啥刚才说laravel的处理比较简单粗暴一些,在嵌套的内层里面实际上是木有真正的事务的,只有最外层一个整体的事务,虽然简单粗暴,但是也解决了在内层新建一个事务时会造成commit的问题。原理就是这个样子了,为了保持完整起见,把commit的代码也copy过来吧!
代码如下:
publicfunctioncommit()
{
if($this->transactions==1)$this->pdo->commit();
–$this->transactions;
}
您可能感兴趣的文章:ThinkPHP3.2.2实现事务操作的方法Thinkphp事务操作实例(推荐)thinkPHP5.0框架事务处理操作简单示例thinkPHP框架中执行事务的方法示例ThinkPHP实现事务回滚示例代码thinkphp多表事务详解php中在PDO中使用事务(Transaction)php+mysql事务rollback&commit示例解析phpmysql事务处理回滚操作(附实例)php事务回滚简单实现方法示例thinkPHP事务操作简单案例分析
MySQL中的if和case语句使用上述就是数据库技术:PHP中实现MySQL嵌套事务的两种解决方案分享的全部内容,如果对大家有所用处且需要了解更多关于mysql数据库学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/dtteaching/911820.html