Laravel中手动控制数据库事务有五种方法:一、DB门面直接调用beginTransaction/commit/rollback;二、try-catch包裹确保异常回滚;三、DB::transaction()闭包自动处理;四、Eloquent外启事务保障模型操作原子性;五、多连接需分别管理事务并协调提交或回滚。

如果您在Laravel应用中需要精确控制数据库操作的原子性,例如在多个模型保存或复杂业务逻辑中确保数据一致性,则可能需要绕过框架默认的自动事务管理,手动调用beginTransaction、commit和rollback。以下是实现手动事务控制的具体方法:
一、使用DB门面手动管理事务
通过DB门面可直接调用底层PDO连接的事务方法,适用于简单、短生命周期的事务场景,且不依赖Eloquent模型事件或观察者行为。
1、在控制器或服务类中引入DB门面:use Illuminate\Support\Facades\DB;
2、调用DB::beginTransaction()启动事务:DB::beginTransaction();
3、执行需要原子性保障的数据库操作,如插入用户记录并关联角色:DB::table('users')->insert(['name' => 'Alice', 'email' => 'a@example.com']);
4、根据业务逻辑判断是否提交:DB::commit();
5、若发生异常或校验失败,显式回滚:DB::rollback();
二、使用try-catch包裹事务块
该方式将事务逻辑封装在异常处理结构中,确保任何未捕获的异常都会触发回滚,避免事务处于悬挂状态,是生产环境中推荐的安全实践。
1、在业务方法内开始事务:DB::beginTransaction();
2、将所有数据库写入操作置于try块中:try { ... }
3、在try块末尾调用提交:DB::commit();
4、在catch块中执行回滚:DB::rollback();
5、重新抛出原始异常或记录日志:throw $e;
三、使用DB::transaction()闭包方式
该方法由Laravel提供,内部自动处理begin、commit与rollback,并在闭包抛出异常时自动回滚,代码更简洁且具备内置错误防护机制。
1、传入一个闭包函数作为参数:DB::transaction(function () { ... });
2、在闭包内执行全部数据库操作,例如更新订单状态并扣减库存:Order::where('id', 1)->update(['status' => 'shipped']);
3、若闭包内未抛出异常,框架自动提交:Stock::decrement('quantity', 1);
4、若闭包中抛出Throwable异常,事务自动终止并回滚:throw new \Exception('Insufficient stock');
5、该闭包支持返回值,返回值即为DB::transaction()的最终返回结果:return $order;
四、在Eloquent模型中结合事务控制
当业务逻辑紧密耦合于模型生命周期(如创建后触发关联保存),可在模型外部开启事务,确保save()、create()等操作被统一纳入事务范围。
1、手动启动事务:DB::beginTransaction();
2、创建主模型实例并保存:$user = User::create(['name' => 'Bob', 'email' => 'b@example.com']);
3、基于刚创建的模型ID执行关联操作:$user->profile()->create(['bio' => 'Developer']);
4、检查关联操作结果是否符合预期:if (!$user->profile) { DB::rollback(); return; }
5、确认无误后提交:DB::commit();
五、跨连接事务控制(多数据库)
当应用涉及多个数据库连接(如MySQL主库+PostgreSQL日志库),需分别对每个连接显式调用事务方法,注意Laravel不自动协调跨连接事务,需自行保证一致性策略。
1、获取主连接并开启事务:$main = DB::connection('mysql'); $main->beginTransaction();
2、获取日志连接并开启事务:$log = DB::connection('pgsql'); $log->beginTransaction();
3、在主连接执行业务写入:$main->table('orders')->insert([...]);
4、在日志连接写入审计记录:$log->table('audit_logs')->insert([...]);
5、仅当两个连接均成功时才提交,任一失败则各自回滚:if ($mainSuccess && $logSuccess) { $main->commit(); $log->commit(); } else { $main->rollback(); $log->rollback(); }










