thinkphp mysql嵌套查询_MySQL 嵌套事务、PHP+MySQL嵌套事务、ThinkPHP 嵌套事务、Laravel 嵌套事务...

本文探讨了MySQL不直接支持的嵌套事务,但可以通过ORM框架如ThinkPHP和Laravel实现模拟。详细解析了这两个框架的事务处理代码,并提供了一种PHP+MySQL的事务多层嵌套实现方式,以及利用savepoint和rollback to实现类似事务嵌套的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MySQL 嵌套事务、PHP+MySQL嵌套事务、ThinkPHP 嵌套事务、Laravel 嵌套事务

在 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.

翻译:

当执行一个START TRANSACTION指令时,会隐式的执行一个commit操作。 所以我们就要在系统架构层面来支持事务的嵌套。

所幸的是在一些成熟的ORM框架中都做了对嵌套的支持,比如 ThinkPHP 和 Laravel等。

如果多层嵌套,transTimes会进行标记。如果嵌套,只有最外层的事务是生效的。这种事务嵌套处理方式跟laravel是一模一样的。

ThinkPHP 6.x 嵌套事务的逻辑代码

文件位置:vendor/topthink/think-orm/src/db/PDOConnection.php

/**

* 启动事务

* @access public

* @return void

* @throws \PDOException

* @throws \Exception

*/

public function startTrans(): void

{

try {

$this->initConnect(true);

++$this->transTimes;

if (1 == $this->transTimes) {

$this->linkID->beginTransaction();

} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {

$this->linkID->exec(

$this->parseSavepoint('trans' . $this->transTimes)

);

}

$this->reConnectTimes = 0;

} catch (\Exception $e) {

if ($this->reConnectTimes < 4 && $this->isBreak($e)) {

--$this->transTimes;

++$this->reConnectTimes;

$this->close()->startTrans();

} else {

throw $e;

}

}

}

/**

* 用于非自动提交状态下面的查询提交

* @access public

* @return void

* @throws PDOException

*/

public function commit(): void

{

$this->initConnect(true);

if (1 == $this->transTimes) {

$this->linkID->commit();

}

--$this->transTimes;

}

/**

* 事务回滚

* @access public

* @return void

* @throws PDOException

*/

public function rollback(): void

{

$this->initConnect(true);

if (1 == $this->transTimes) {

$this->linkID->rollBack();

} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {

$this->linkID->exec(

$this->parseSavepointRollBack('trans' . $this->transTimes)

);

}

$this->transTimes = max(0, $this->transTimes - 1);

}

Laravel 8.x 嵌套事务的逻辑代码

文件位置:/laravel/framework/blob/8.x/src/Illuminate/Database/DatabaseTransactionsManager.php

/**

* Start a new database transaction.

*

* @param string $connection

* @param int $level

* @return void

*/

public function begin($connection, $level)

{

$this->transactions->push(

new DatabaseTransactionRecord($connection, $level)

);

}

/**

* Rollback the active database transaction.

*

* @param string $connection

* @param int $level

* @return void

*/

public function rollback($connection, $level)

{

$this->transactions = $this->transactions->reject(function ($transaction) use ($connection, $level) {

return $transaction->connection == $connection &&

$transaction->level > $level;

})->values();

}

/**

* Commit the active database transaction.

*

* @param string $connection

* @return void

*/

public function commit($connection)

{

$this->transactions = $this->transactions->reject(function ($transaction) use ($connection) {

if ($transaction->connection == $connection) {

$transaction->executeCallbacks();

return true;

}

return false;

})->values();

}

文件位置:/laravel/framework/blob/8.x/src/Illuminate/Database/DatabaseTransactionRecord.php

namespace Illuminate\Database;

class DatabaseTransactionRecord

{

/**

* The name of the database connection.

*

* @var string

*/

public $connection;

/**

* The transaction level.

*

* @var int

*/

public $level;

/**

* The callbacks that should be executed after committing.

*

* @var array

*/

protected $callbacks = [];

/**

* Create a new database transaction record instance.

*

* @param string $connection

* @param int $level

* @return void

*/

public function __construct($connection, $level)

{

$this->connection = $connection;

$this->level = $level;

}

/**

* Register a callback to be executed after committing.

*

* @param callable $callback

* @return void

*/

public function addCallback($callback)

{

$this->callbacks[] = $callback;

}

/**

* Execute all of the callbacks.

*

* @return void

*/

public function executeCallbacks()

{

foreach ($this->callbacks as $callback) {

call_user_func($callback);

}

}

/**

* Get all of the callbacks.

*

* @return array

*/

public function getCallbacks()

{

return $this->callbacks;

}

}

php+mysql 事务多层嵌套的实现方式

msyql 本身不支持事务嵌套的,但是可以按照嵌套事务的思路变相实现事务多层嵌套。

开启事务时 先 mark 一个标志,每嵌套一次,就将该值加 1,但是开启事务这个操作只在 mark=1 时才真的去实现,其他只是累加。

而提交时,肯定是从最内层开始提交,每提交一次,mark 减去 1,直到 mark=1 时,才真的去实现提交。

回滚也是如此。

代码如下:

public function beginTransaction() {

++$this->transactions;

if ($this->transactions==1){

$this->pdo->beginTransaction();

}

}

public function rollBack() {

if ($this->transactions ==1) {

$this->transactions ==0;

$this->pdo->rollBack();

} else {

--$this->transactions;

}

}

public function commit() {

if ($this->transactions ==1){

$this->pdo->commit();

}

--$this->transactions;

}

MySQL 使用 savepoint 和 rollback to 的语句实现事务嵌套

在 MySQL 多次开启事务,出现了数据错乱问题,伪代码如下:

begin;

# 操作逻辑代码块1

begin;

# 操作逻辑代码块2

rollback;

执行完后出现了操作1的数据真正写入,只有操作2的数据回滚了。在第一个事务没有提交或回滚时,再开启第二个事务时,会自动提交第一个事务。

这明显不符合心理预期,而且也无法回滚一部分操作。那么问题来了,MySQL 支不支持事务嵌套呢?

这个问题很难准确回答支持还是不支持!

首先,调用多次begin的写法,在 MySQL 里肯定是无法首先事务嵌套的。经过群内一位朋友的提醒,了解到 MySQL 中有一个叫 savepoint 和 rollback to 的语句。

示例代码:

DROP TABLE IF EXISTS `test`;

CREATE TABLE `test` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`name` varchar(255) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

begin;

insert into `test`(`name`) values('111');

SAVEPOINT p1;

insert into `test`(`name`) values('222');

ROLLBACK TO p1;

commit;

最终执行结果,test表中只有 111 这个数据,实现了部分操作的回滚操作。同理也避免了多次开启事务,导致前一个事务被提交的问题。

可能savepoint和rollback to语句并不能称之为事务嵌套,也不能说 MySQL 是支持还是不支持事务嵌套。总之通过savepoint和rollback to,是可以用来达到一些事务嵌套特性的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值