codeigniter 4 事务必须用 transstart() 和 transcomplete() 手动包裹,二者缺一不可;transstatus() 是判断提交成败的唯一可靠方式,需在 transcomplete() 后立即检查;不支持嵌套事务,所有操作须共用同一数据库实例;生产环境应关闭 db_debug 但开启 log_threshold 记录错误。

事务必须用 transStart() 和 transComplete() 包裹
CodeIgniter 4 不再支持像 Laravel 那样用闭包自动管理事务,也不提供 DB::transaction() 这类语法糖。事务控制完全依赖手动开启与结束:必须先调用 $this->db->transStart(),所有数据库操作写在它之后、$this->db->transComplete() 之前。这两个方法成对出现,缺一不可。
常见错误是只调用 transStart() 忘了 transComplete(),或者把查询写在它们之外——这时事务根本不会生效,每条语句都自动提交。
-
transStart()不会立即开启数据库事务,只是标记“接下来的操作要纳入事务” -
transComplete()才真正执行COMMIT或ROLLBACK,依据是期间是否有失败的查询 - 只要任意一条
insert()、update()、delete()返回FALSE,transComplete()就自动回滚
transStatus() 是判断是否成功提交的唯一可靠方式
很多人误以为检查 affected_rows() 或捕获异常就能知道事务结果,但在 CI4 中这不可靠。CI4 的事务机制不抛出异常,而是静默失败并回滚。真正能告诉你“这次事务到底有没有提交成功”的,只有 $this->db->transStatus()。
它返回布尔值:TRUE 表示全部操作成功且已提交,FALSE 表示至少一条失败、已回滚。你必须在 transComplete() 后立刻检查它:
$this->db->transStart();
$this->db->table('orders')->insert($order_data);
$this->db->table('order_items')->insertBatch($items);
$this->db->transComplete();
if ($this->db->transStatus() === FALSE)
{
// 回滚已发生,这里做错误处理,比如返回 JSON 错误或跳转
return $this->fail('订单创建失败');
}
注意:transStatus() 只在 transComplete() 调用后有效,之前调用返回 NULL;且每个请求只能用一次事务块,重复调用会覆盖状态。
嵌套事务不被支持,多模型操作需统一用一个 $db 实例
CI4 没有真正的嵌套事务(savepoint),如果你在事务块里又调用了另一个也含 transStart() 的方法,外层事务会被干扰,transStatus() 可能失准。所有相关操作必须在同一个数据库连接实例上完成。
这意味着:不要在事务中调用不同模型的 save() 方法(除非它们共享同一个 $db),也不要手动 new Model() 再调它的写操作——Model 默认用的是独立连接实例,会脱离当前事务上下文。
- 安全做法:所有写操作都直接通过
$this->db进行,或确保模型构造时传入同一连接(new UserModel($this->db)) - 避免在事务内调用
$model->insert(),改用$this->db->table('users')->insert(...) - 如果必须用模型,CI4.5 推荐显式注入连接:
public function __construct(protected Database $db) { ... }
生产环境务必关闭 db_debug,但保留 log_threshold 记录失败事务
开启 db_debug 会让事务失败时直接输出 SQL 错误页,掩盖真实的业务逻辑错误,且暴露敏感信息。但它关掉后,你又很难定位哪条语句导致回滚。
折中方案是:设 'db_debug' => false,同时打开数据库日志,让失败事务留下痕迹:
// application/Config/Database.php 'log_threshold' => 2, // 记录所有错误和警告 'log_path' => WRITEPATH . 'logs/db/', // 确保目录可写
这样,当 transStatus() 返回 FALSE 时,你可以立刻去 writable/logs/db/ 查对应时间戳的日志,看到具体哪条 SQL 出错、错误码是什么。这是 CI4 事务调试最实际的路径。
真正容易被忽略的是:事务失败时,$this->db 不会自动重置状态,后续查询仍可能受残留影响;所以每次事务结束后,建议明确重新获取连接或清空查询缓存($this->db->resetQuery()),尤其在长生命周期的服务中。











