tp5或tp5.1中启用了事务,但没有正确回滚

揭示TP5或TP5.1中事务处理的常见误区,即即使SQL执行未引发错误但未影响任何行时,事务仍会提交。文章提供了解决方案,包括手动事务管理及如何确保SQL执行的有效性。

tp5或tp5.1中的自动事务没有正确回滚问题

在tp5或tp5.1中的事务,无论自动事务还是手动事务,只有sql执行报错了才会中断执行并回滚,但如果sql没报错,只是影响行数为0(什么都没插入或更新),事务最后是会被提交的!

这个在用tp5或5.1的事务的时候务必注意,不然就会出现用了事务,但依然把错误数据写入的情况。

解决方案:

//首先,全部事务都改用手动事务,不要用自动事务
// 启动事务
Db::startTrans();
try{
	//然后在每段sql后都判断返回结果是否为true,如果不为true就马上抛出错误,如(以tp5为例):
	$xxx = new XxxModel();
	$res= $xxx->insert($data);
	if(!$res && $xxx->getChangedData()){
		return json(['code'=>-1,'msg'=>'sql执行失败了!']))
	}
    
    // 提交事务
    Db::commit();    
} catch (\Exception $e) {
    // 回滚事务
    Db::rollback();
}

2020-04-11更新:
可能会有同学对这种场景比较少遇到,而我也从今天的评论中发现我的确没有详细说明具体场景,所以这里更一下,说下我当时是具体什么场景下遇到这种情况的:
首先明确:当时的引擎是inndb,并不是myisam,是支持事务的。
场景:假设事务的try catch中有两条sql语句而且两条sql语句都是正确的sql语句,但我希望A语句如果没有写入任何东西,B语句也不要执行。如果对thinkphp的执行结果进行判断,A语句可能会因为数据的确没有任何变动或者其他原因的确没有任何写入或更新操作,而B语句依然正常执行了。对thinkphp来说,数据因为没有变动所以影响行数为0并不是错误,只要sql语句能正常执行无论影响行数是否为0,都不算错误,都不会回滚。

这个笔记是我很久之前写下的,也是刚接触thinkphp时遇到的,老实说也很少遇到上述的场景,只是的确发生过这种情况。后来经过看thinkphp的源码,模型类中其实是有getChangedData()方法(这个方法在手册中并未提及,全文搜索也搜不出来)能判断到底有没数据变动,所以上面的示例代码中的if判断中也增加了getChangedData()的判断。

<think>好的,我现在需要帮助用户理解ThinkPHP 5.1框架中事务、锁、回滚的具体行为以及它们对数据库操作的影响。首先,我需要回顾一下用户提供的引用内容,整理出相关的知识点。 首先,用户提供的引用1提到ThinkPHP5.1处理事务嵌套的问题,以及MySQL是否支持事务嵌套。引用2指出事务处理需要数据库引擎支持,比如InnoDB。引用3和5展示了TP5.1使用事务的基本方法,通过transaction方法自动处理回滚。引用4讨论了XA事务中的状态问题,回滚时需要事务处于非active状态。 接下来,我需要将这些信息整合,分步骤说明事务、锁和回滚的行为。可能需要先解释事务的基本概念,然后讨论锁机制,接着是回滚的条件和限制,特别是嵌套事务和分布式事务的情况。 事务嵌套方面,根据引用1TP5.1对嵌套事务进行了处理,可能使用了保存点(savepoint)来实现。但MySQL本身是否支持呢?需要确认这一点。实际上,MySQL的InnoDB支持保存点,允许在事务内设置保存点,并在回滚时指定到某个保存点,而不是全部回滚。这可能就是TP5.1处理嵌套事务的方式,但如果回滚没有正确指定保存点,就会出现错误,如引用1中的问题。 关于锁,用户可能想知道在事务中如何加锁,比如共享锁排他锁。在TP5.1中,可以使用lock方法,例如在查询时添加->lock(true)来加锁,确保在事务过程中数据的一致性。 回滚的条件,引用3和5指出,当使用transaction方法时,闭包中的异常会自动触发回滚。而手动事务需要显式调用commitrollback。此外,引用4提到的XA事务需要注意事务状态,不能在active状态下回滚,这可能涉及到分布式事务的复杂性,需要确保所有参与事务的节点状态正确。 还需要注意数据库引擎的支持,如引用2提到的MyISAM不支持事务,必须使用InnoDB。此外,事务的隔离级别可能影响锁的行为和并发问题,但用户的问题中没有直接提到,可能需要简单提及。 综合这些信息,回答的结构应该是分点解释事务、锁、回滚的行为,结合TP5.1的具体实现和MySQL的特性,并引用对应的引用来源。最后生成相关问题,帮助用户深入理解。 在组织内容时,要确保使用正确的中文表达,行内公式用$...$,独立公式用$$...$$,但这里可能数学公式较少,主要是代码示例和框架方法。需要检查是否有需要LaTeX表达的部分,例如数据库事务的ACID属性,可以用行内公式,如$ACID$。 最后,生成的相关问题应该围绕事务嵌套、锁的使用、异常处理、分布式事务等,基于用户提供的引用和回答内容。</think>### ThinkPHP 5.1 事务、锁与回滚行为解析 #### 1. **事务基本行为** - **事务启动与提交** 在 TP5.1 中,使用 `Db::startTrans()` 手动启动事务,`Db::commit()` 提交事务。若使用 `Db::transaction()` 闭包方式,异常会自动触发回滚,否则需手动调用 `Db::rollback()`[^3][^5]。 - **嵌套事务实现** TP5.1 通过 MySQL 的 **保存点(SAVEPOINT)** 模拟嵌套事务。例如: ```php Db::startTrans(); try { // 主事务操作 Db::startTrans(); // 实际创建保存点 // 子事务操作 Db::commit(); // 释放保存点 Db::commit(); // 提交主事务 } catch (\Exception $e) { Db::rollback(); // 回滚至保存点事务 } ``` 若回滚时未正确匹配保存点,会报错 `SAVEPOINT ... does not exist`[^1][^4]。 #### 2. **锁机制与并发控制** - **显式加锁** 通过 `->lock(true)` 在查询时添加 **排他锁**(如 `SELECT ... FOR UPDATE`),确保事务期间数据不被其他会话修改: ```php Db::table('user')->where('id', 1)->lock(true)->find(); ``` 需在事务使用,否则锁会立即释放。 - **锁与隔离级别** MySQL 的隔离级别(如 `REPEATABLE READ`)影响锁的范围和持续时间。TP5.1 默认依赖数据库配置,可通过 `Db::query("SET TRANSACTION ISOLATION LEVEL ...")` 调整。 #### 3. **回滚触发条件** - **自动回滚** `Db::transaction()` 闭包内抛出异常时,自动回滚整个事务[^3]。 - **手动回滚** 需严格匹配 `startTrans()` 与 `rollback()` 的层级,否则可能破坏事务原子性[^1]。 - **分布式事务(XA)** XA 事务需保证所有子事务状态一致。若某子事务处于 `ACTIVE` 状态,直接回滚会失败,需先终止未完成的操作[^4]。 #### 4. **对数据库的影响** - **引擎依赖** 仅 InnoDB 引擎支持事务和行级锁,MyISAM 不支持[^2]。 - **性能影响** 长事务高频加锁可能导致阻塞,需合理设计事务范围和锁粒度。 #### 5. **典型问题场景** - **嵌套事务深度不匹配** 错误示例: ```php Db::startTrans(); Db::startTrans(); Db::commit(); // 释放保存点 Db::rollback();// 尝试回滚事务,但保存点已释放 → 报错 ``` - **跨表事务一致性** 多表操作需在同一事务中,避免部分提交: ```php Db::transaction(function(){ Db::table('user')->delete(1); Db::table('order')->where('user_id',1)->delete(); }); ``` ### 代码示例 ```php // 事务+锁的典型用法 Db::startTrans(); try { // 加排他锁查询用户 $user = Db::table('user')->where('id', 1)->lock(true)->find(); // 更新余额 Db::table('user')->where('id', 1)->update(['balance' => $user['balance'] - 100]); Db::commit(); } catch (\Exception $e) { Db::rollback(); throw $e; } ``` ### 注意事项 1. 确保所有数据库操作表使用 InnoDB 引擎。 2. 避免在事务中执行耗时操作(如网络请求),减少锁竞争。 3. XA 事务需额外处理 `PREPARE` 和 `COMMIT` 阶段的状态。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值