laravel项目中出现mysql嵌套事务的分析

来源:互联网 发布:centos如何配置vim 编辑:程序博客网 时间:2024/06/05 02:37
自己对于项目代码架构了解的真的是太少了,也没有时间去看、研究,整天忙于各种业务代码。今天想在项目中添加上事务处理!碰到了如下的一个问题:1.定义了一个公共方法,里面进行了一系列的数据库操作,为了保证异常时,可以恢复到最初状态,采用了 '事务机制'。// 增加用户积分function add_point(){    DB::beginTransaction();    try{    // 各种操作    DB::commit();    } catch (\Exception $e) {    DB::rollBack();    } catch (\Throwable $e) {    DB::rollBack();    }}    2.当在其他方法内,同样进行了一系列数据操作,又使用了 '事务机制',同时,使用了之前定义了的公共方法 'add_point()'function register(){    DB::beginTransaction();    try{    // 1.先增加用户积分    add_point();    // 2.其他各种操作    DB::commit();    } catch (\Exception $e) {    DB::rollBack();    } catch (\Throwable $e) {    DB::rollBack();    }}3.发现了问题:出现了2次事务机制,且是嵌套的2个事务!这种情况可以吗???(我是真心不知道。。。所以说,架构的一些东西,差的太多了,还没人问,只能自己摸索!真希望有个大牛师父,我保证跟着他好好学习、提高!)只能百度、google,发现了一篇博客,这里引用下:PHP中实现MySQL嵌套事务的两种解决方案(http://blog.csdn.net/hello_katty/article/details/45220825)/*1.非常感谢这些大牛,解决了我的困惑,而且里面提到了目前项目中用的 laravel 框架!2.再一次感谢laravel这么好的框架,这些牛人的架构,让人敬畏! */4.问题分析(这里会复制一些上面博客的内容)1>在MySQL的官方文档中有明确的说明不支持嵌套事务:Transactions cannot be nested. This is a consequence of the implicit commit performed for any current transaction when you issue a START TRANSACTION statement or one of its synonyms. 翻译过来的意思就是:MySQL不可嵌套事务。一旦嵌套了事务,当第二次 '开启事务' 时,会隐式(默认)执行上一个事务的 '事务提交'。/*这种情况不是我们想要的!MySQL本身不支持! */2>所幸的是在一些成熟的ORM框架中都做了对嵌套的支持,比如doctrine或者laravel。我们可以通过程序,来模拟出 '事务嵌套'。这里我简单看了下laravel关于数据库的源码:/*我只想说laravel太复杂了,真的是我这种人努力一辈子也达不到的境界 */1)目录分析:laravel数据库源码目录为:vendor/laravel/framework/src/Illuminate/Database/Capsule/// 注册服务容器Connectors/// 数据库连接(支持mysql,postgres,sqlite,sqlserver)Console/// 命令行Migrations/// 迁移Seeds/// 填充Eloquent/// Eloquent ORMEvents/// 事件处理Migrations/// 迁移Query/// 查询构建器Schema/// 表、字段操作connection.php // 数据库连接、基本操作核心文件(table, select, update, insert, delete,事务等...)...剩余文件...2)在connection.php中,找到事务处理相关的代码:    public function transaction(Closure $callback)    {        $this->beginTransaction();        // We'll simply execute the given callback within a try / catch block        // and if we catch any exception we can rollback the transaction        // so that none of the changes are persisted to the database.        try {            $result = $callback($this);            $this->commit();        }        // If we catch an exception, we will roll back so nothing gets messed        // up in the database. Then we'll re-throw the exception so it can        // be handled how the developer sees fit for their applications.        catch (Exception $e) {            $this->rollBack();            throw $e;        } catch (Throwable $e) {            $this->rollBack();            throw $e;        }        return $result;    }    public function beginTransaction()    {        ++$this->transactions;        if ($this->transactions == 1) {            try {                $this->getPdo()->beginTransaction();            } catch (Exception $e) {                --$this->transactions;                throw $e;            }        } elseif ($this->transactions > 1 && $this->queryGrammar->supportsSavepoints()) {            $this->getPdo()->exec(                $this->queryGrammar->compileSavepoint('trans'.$this->transactions)            );        }        $this->fireConnectionEvent('beganTransaction');    }    public function commit()    {        if ($this->transactions == 1) {            $this->getPdo()->commit();        }        $this->transactions = max(0, $this->transactions - 1);        $this->fireConnectionEvent('committed');    }    public function rollBack()    {        if ($this->transactions == 1) {            $this->getPdo()->rollBack();        } elseif ($this->transactions > 1 && $this->queryGrammar->supportsSavepoints()) {            $this->getPdo()->exec(                $this->queryGrammar->compileSavepointRollBack('trans'.$this->transactions)            );        }        $this->transactions = max(0, $this->transactions - 1);        $this->fireConnectionEvent('rollingBack');    }    public function transactionLevel()    {        return $this->transactions;    }    3)简单解释下:    laravel5.2,事务文档:    http://laravelacademy.org/post/2942.html    相关的几个方法:    自动事务:    DB::transaction();    手动事务:    DB::beginTransaction();    DB::rollBack();    DB::commit();   其实提到的就是上面源码的几个函数    DB::transactions(); 只是将 '手动事务' 的3个方法封装了下,同时包含了异常处理!    /*    关于异常处理,为什么如此处理,查看我之前写的异常处理分析:    laravel异常分析(http://blog.csdn.net/beyond__devil/article/details/78327433)     */    所以,我们不想用自动事务,传递 '闭包函数',自己来使用异常定制:    DB::beginTransaction();    try{    // 各种操作    DB::commit();    } catch (\Exception $e) {    DB::rollBack();    } catch (\Throwable $e) {    DB::rollBack();    }    4>主要要解释下,事务嵌套的实现:    主要是通过 '$this->transactions' 来判断:    如果 $this->transactions == 1 // 非事务嵌套,正常处理即可    如果 $this->transactions > 1// 事务嵌套,通过是否支持 '不同嵌套等级的存储点',来进行不同的回滚!    vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php    最终发现了    // 支持 'savepoint'    public function supportsSavepoints()    {        return true;    }    // 标记一个 'savepoint trans2'    public function compileSavepoint($name)    {        return 'SAVEPOINT '.$name;    }    // 回滚到 'savepoint trans2'    public function compileSavepointRollBack($name)    {        return 'ROLLBACK TO SAVEPOINT '.$name;    }/*搜索了好多代码,以为是laravel框架自己实现的!最终发现,这是 MySQL '部分事务' 的语句! */到此,我们基本就明白了原理!

原创粉丝点击