自己对于项目代码架构了解的真的是太少了,也没有时间去看、研究,整天忙于各种业务代码。今天想在项目中添加上事务处理!碰到了如下的一个问题:
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 ORM
Events/ // 事件处理
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 '部分事务' 的语句!
*/
到此,我们基本就明白了原理!
laravel项目中出现mysql嵌套事务的分析
最新推荐文章于 2024-09-18 09:40:34 发布