CodeIgniter 3 的 trans_complete() 不自动回滚,因其仅监听自身 query() 调用失败;跨函数调用、严格模式关闭、驱动不支持事务或表引擎非 InnoDB 均会导致回滚失效,需显式检查与手动回滚。

CodeIgniter 3 的 trans_complete() 不自动回滚?检查严格模式
默认开启严格模式,$this->db->trans_complete() 会在任意一个内部 query() 失败时自动触发 trans_rollback()。但如果你调用的是其他函数(比如 $this->utils->insert_function()),而这些函数内部又执行了独立的 $this->db->query(),事务状态就可能被意外覆盖——因为 CodeIgniter 只监听自己直接调用的 query() 方法是否出错。
关键点在于:_trans_status 属性只在 query() 执行失败时被设为 FALSE;如果错误发生在封装函数里、且没抛异常也没显式调用 query(),这个标志就不会变。
- 确认是否启用了严格模式:
$this->db->trans_strict(TRUE)(CI3 默认为TRUE,但某些旧配置或手动关闭过) - 禁用严格模式后,即使一组事务失败,也不会影响后续同级事务组——这常被误认为“没回滚”,其实是设计行为
- 不要依赖
trans_complete()的返回值判断成败:trans_complete()总是返回TRUE,它只负责收尾,不反映结果
跨函数调用时事务失效:必须统一由主事务控制流捕获错误
你在 trans_start() 和 trans_complete() 之间调用 $this->utils->insert_function(),而该函数内部又调用了 $this->db->insert() —— 这本身没问题,但前提是这个 insert() 真的执行了,并且失败时能让外层知道。
CI3 的 insert()、update() 等 Query Builder 方法,在底层仍会走 query(),所以只要没被 try/catch 吞掉异常、也没手动 return false,_trans_status 就会被正确设置。
- 确保工具函数不屏蔽数据库错误:不要在
insert_function()里用@$this->db->insert()或空 catch - 避免在工具函数里调用
$this->db->trans_start()或$this->db->trans_complete()—— 嵌套事务在 CI3 中靠_trans_depth计数,但外层无法感知内层失败 - 最稳妥做法:让每个工具函数返回布尔值,主流程显式检查:
$this->db->trans_start(); $result = $this->utils->insert_function($data); if ($result === FALSE) { $this->db->trans_rollback(); } else { $result = $this->utils->update_function2($test); if ($result === FALSE) { $this->db->trans_rollback(); } } $this->db->trans_complete();
为什么 trans_rollback() 没生效?查连接与驱动兼容性
即使你写了 $this->db->trans_rollback(),也可能什么都没发生——常见原因是数据库驱动不支持事务,或表类型不支持(比如 MySQL 的 MyISAM 引擎)。
CI3 在初始化连接时会根据 DBDriver 自动判断事务能力,但不会主动报错。例如 SQLite 或某些 PDO 配置下,trans_enabled 可能为 FALSE 却不提示。
- 运行
var_dump($this->db->trans_enabled)确认事务开关已打开 - 检查当前表引擎:
SHOW CREATE TABLE users;,确保是 InnoDB - MySQL 用户注意:如果开启了
autocommit=1且未被 CI 正确覆盖,ROLLBACK会静默失败 - 部分云数据库(如阿里云 RDS 只读实例)默认禁用事务,需确认实例角色
CI4 事务更可靠,但仍有边界条件要处理
CI4 改用 $db->transBegin() / $db->transCommit() / $db->transRollback() 显式控制,不再依赖自动状态跟踪,逻辑更透明。但「自动回滚」依然不存在——你仍得自己写异常捕获。
推荐用 try/catch 包裹:
$db = \Config\Database::connect();
$db->transBegin();
try {
$db->table('users')->insert($data);
$db->table('logs')->insert(['action' => 'user_created']);
if (! $db->transComplete()) {
throw new Exception('Transaction failed');
}
} catch (Exception $e) {
$db->transRollback();
log_message('error', 'TX rollback: ' . $e->getMessage());
// 返回错误响应
}
真正容易被忽略的是:事务只对当前数据库连接有效,如果你在事务中调用了另一个模型、而那个模型用了独立的 $db2 = \Config\Database::connect('other'),它的操作完全游离于事务之外——这种跨连接操作,无论 CI3 还是 CI4,都不可能被回滚。


















