MySQL不支持嵌套事务? ThinkPHP和Laravel嵌套设计支持
MySQL官方文档明确指出不支持嵌套事务:
事务不能嵌套。这是当您发出 START TRANSACTION 命令或其同义词之一时对任何当前事务执行隐式提交的结果。因此,我们需要在系统架构层面支持事务嵌套。
幸运的是,一些成熟的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();
}
/**
* 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 php+mysql 如何实现 multi级事务嵌套
msyql本身不支持事务嵌套,但是它可以伪装成嵌套事务的思想来实现多级事务嵌套。
启动事务时,先标记flag,每次嵌套时该值加1,但只有mark=1时才执行事务启动操作,其他只是累加。
发送时必须从最内层开始。每次提交时,grade都会减1,直到grade=1时才真正执行提交。
退货也是如此。 ?操作 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;
最终执行结果,测试
表中只有数据111,实现了返回部分操作的操作。同理,也避免了多个事务发起,导致前一个事务提交的问题。
可能的savepoint
和回滚到
命令不能称为嵌套事务,也不能说MySQL是否支持嵌套事务。简而言之,通过保存点
和回滚到
可以用来实现一些事务嵌套功能。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。