CI4事务必须手动配对transStart()和transComplete(),所有操作须在其间执行;仅靠transStatus()判断成败,且需检查其返回值并中断后续流程以防数据不一致。

CI4 事务必须手动配对 transStart() 和 transComplete()
CodeIgniter 4 不支持自动事务封装,transStart() 和 transComplete() 必须成对出现,且所有数据库操作必须夹在两者之间。漏掉任意一个,或把查询写在它们外面,事务就形同虚设——每条语句都会立刻提交。
常见错误包括:
- 只调用
transStart(),忘了transComplete() - 在
transComplete()之后又执行了insert()或update() - 误以为
transStart()立即开启数据库事务(其实它只是打标记,真正提交/回滚由transComplete()触发)
判断事务是否成功只能靠 transStatus()
transStatus() 是唯一可信的事务结果信号。它返回 TRUE 表示全部操作成功并已提交,FALSE 表示至少一条失败、已回滚。不能依赖 affected_rows()、返回值或异常捕获来判断——CI4 的事务机制不抛异常,失败是静默的。
注意:
-
transStatus()只在transComplete()调用后有效,之前调用返回NULL - 每个请求生命周期内只能用一次事务块,重复调用会覆盖前一次状态
- 不能在事务块里嵌套另一个
transStart(),CI4 不支持 savepoint 或嵌套事务
多模型操作必须共用同一个 $db 实例
如果你在事务中调用多个 Model 的 save() 或写操作,很可能失效。因为默认情况下每个 Model 都会创建独立的数据库连接实例,脱离当前事务上下文。
正确做法是:
- 所有操作统一通过控制器或服务类持有的
$this->db实例执行(例如$this->db->table('orders')->insert(...)) - 避免在事务中 new Model() 或调用其写方法;如需复用逻辑,改用普通方法 + 共享
$db - 不要跨不同
$db实例混用事务,否则transStatus()失准,回滚可能只作用于部分表
自定义类中事务回滚的典型结构
在自定义业务类(非 Controller)里使用事务时,必须显式传入或注入 $db 实例,并严格遵循“标记-执行-判断”流程:
// 示例:OrderService.php 中的方法
public function createOrder($orderData, $items)
{
$db = $this->db; // 确保复用同一实例
$db->transStart();
$db->table('orders')->insert($orderData);
$db->table('order_items')->insertBatch($items);
$db->transComplete();
if ($db->transStatus() === FALSE) {
// 注意:这里只是发现失败,不是重试入口
// 通常应直接 throw Exception 或返回错误码
throw new RuntimeException('订单创建事务失败');
}
return $db->insertID();
}
最易被忽略的是:事务失败后没中断执行流程。仅调用 transComplete() 不代表业务成功;必须检查 transStatus() 并主动终止后续逻辑,否则可能引发数据不一致或空 ID 引用。

















