必须使用innodb引擎并统一db门面开启事务,否则事务静默失效;推荐用db::starttrans()配合try-catch,确保两模型共用同一连接,异常时db::rollback()回滚。

刚接触ThinkPHP的新手常遇到这样的问题:用户注册时要同时写入users表和profiles表,中间任何一个失败,整个操作就得全部撤销,否则数据库就会出现半截数据。这必须靠事务来兜底,但直接在模型里调用startTrans却总报错或不生效。
确认事务前提条件
先检查数据库配置是否启用InnoDB引擎——MyISAM不支持事务,哪怕代码写得再对,【事务也会静默失效】。打开config/database.php,确认'mysql'连接下的'engine'参数未被设为'myisam',或干脆删掉这一行让系统自动选用默认引擎。
运行php think db:info命令,查看当前连接实际使用的存储引擎,输出中必须包含InnoDB字样。
在模型类中正确开启事务
方法一:使用静态模型调用(推荐新手)
在控制器中直接调用模型的静态方法,例如UserModel::startTrans()。这要求UserModel类已定义且继承自think\Model,不能是空模型或基础Model实例。
方法二:实例化后调用(需注意作用域)
$user = new UserModel(); $user->startTrans(); 这一步看似可行,但若后续commit或rollback不在同一对象上调用,事务上下文会丢失——【跨实例调用事务方法必然失败】。
完整事务流程与异常处理
第一步:统一用Db门面开启事务,避免混用模型静态方法和Db类
use think\facade\Db;
第二步:将所有数据库操作包裹在try-catch中
Db::startTrans();
try {
UserModel::create(['name' => '张三', 'email' => 'zhang@example.com']);
ProfileModel::create(['user_id' => 1, 'bio' => '开发者']);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
第三步:确保两个模型操作共用同一Db连接实例——如果ProfileModel内部手动指定了不同数据库配置,事务会立即断裂,此时回滚只影响UserModel部分。
验证事务是否真正生效
故意在第二个操作前抛出异常:在ProfileModel::create()之前加一句throw new \Exception('模拟失败');,然后执行接口。检查数据库users表是否为空——如果不为空,说明事务未生效,大概率是数据库引擎或连接复用出了问题。
这一步操作起来很简单,直接把异常语句加进去跑一次就行。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











