ThinkPHP 5.1 中事务必须用 Db::transaction() 闭包方式,手动 startTrans() 因连接惰性初始化极易失效;需确保 InnoDB 引擎、关闭 autocommit、异常穿透且不混用连接。

ThinkPHP 5.1 中事务处理必须用 Db::transaction() 闭包方式,手动调用 startTrans() + commit()/rollback() 在绝大多数场景下已失效。
必须用 Db::transaction() 闭包
这是唯一可靠写法,它能确保事务指令与 SQL 执行落在同一 PDO 连接上,自动处理异常回滚和成功提交:
- 闭包内任意位置抛出未捕获的
\Exception(包括 PHP Notice 触发的ErrorException),事务自动回滚 - 闭包正常执行结束,自动提交;无需手写
try/catch,也不要在闭包里吞异常 - 想提前退出并回滚?直接
return false;(TP 6.0.10+ 支持,TP5.1 建议仍靠异常触发) - 错误示例:
Db::transaction()单独一行——这不开启事务,只是返回一个对象
确保数据库层支持事务
代码正确 ≠ 事务生效,还需检查底层配置:
- 表引擎必须是 InnoDB:执行
SHOW CREATE TABLE 表名,确认含ENGINE=InnoDB;MyISAM 表在事务中执行会立刻落库,rollback()对其无效 - 自动提交必须关闭:可通过
Db::connect()->getPdo()->getAttribute(PDO::ATTR_AUTOCOMMIT)检查,返回值应为0 - 避免混用 InnoDB 和 MyISAM 表:哪怕只有一条语句操作 MyISAM 表,该语句就脱离事务控制
- 事务中禁止 DDL(如
ALTER TABLE)、禁止非事务性查询(如未加FOR UPDATE的SELECT),某些驱动下会隐式提交
嵌套事务可用但有边界
ThinkPHP 5.1 支持事务嵌套,底层通过 SAVEPOINT 实现:
立即学习“PHP免费学习笔记(深入)”;
- 内部
Db::transaction()不会真正开启新事务,而是创建保存点(如SAVEPOINT trans2) - 内部闭包异常时,只回滚到该保存点,不影响外层逻辑
- 外层事务最终提交或回滚,才决定全部数据是否持久化
- 注意:MySQL 本身不支持真正嵌套事务,Savepoint 是模拟方案,不能替代业务层的逻辑拆分
隔离级别可选但需传对参数
Db::transaction() 支持传入第二个参数指定隔离级别:
- 合法值为框架常量:
Db::TRANSACTION_READ_UNCOMMITTED、Db::TRANSACTION_READ_COMMITTED、Db::TRANSACTION_REPEATABLE_READ、Db::TRANSACTION_SERIALIZABLE - 传字符串字面量(如
'SERIALIZABLE')也有效,但大小写敏感;传错(如'read committed')不会报错,PDO 将静默降级为默认级别 - MySQL 对部分隔离级别的实际行为与标准略有差异,生产环境建议优先使用默认的
REPEATABLE READ



















