如何在Laravel中实现嵌套数据库事务?满足API失败仅存日志需求
Laravel实现嵌套事务:仅保留API日志,回滚其他业务操作
要实现API请求失败时回滚主业务SQL,但保留API日志的需求,核心是让日志操作脱离主事务的上下文。因为Laravel的事务是基于数据库连接的,同一连接下的所有操作都会受当前事务影响,所以我们需要用独立的数据库连接来处理日志保存。
实现步骤
1. 配置独立的数据库连接
在config/database.php的connections数组中,复制一份现有数据库连接(比如mysql),命名为mysql_log,使用相同的数据库配置:
'connections' => [ // 原有业务连接 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), // ... 其他配置 ], // 新增日志专用连接 'mysql_log' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), // ... 其他配置和mysql一致 ], ],
2. 修改主业务逻辑
主事务只处理业务SQL,API失败时回滚主事务即可:
DB::beginTransaction(); // 业务SQL操作,这些会在API失败时被回滚 // ... 比如创建订单、更新库存等操作 ... $requestApi = RequestApi::send($data); if(!$requestApi->status) { DB::rollBack(); // 回滚主事务的所有业务操作 return response()->json(['error' => 'Error message'], 500); } DB::commit(); // 提交主事务,确认业务操作
3. 调整RequestApi类的日志保存逻辑
在send方法中,使用独立的数据库连接保存日志,确保日志操作不受主事务影响:
class RequestApi { public static function send($data) { // 执行实际的API请求 $apiResponse = // 调用第三方API的逻辑,比如用Guzzle发送请求 // 使用独立连接保存API日志 $logConnection = DB::connection('mysql_log'); $logConnection->beginTransaction(); try { // 插入日志记录,指定使用日志连接 ApiLog::on('mysql_log')->create([ 'request_data' => json_encode($data), 'response_data' => json_encode($apiResponse), 'status' => $apiResponse['status'] ?? false, 'created_at' => now() ]); $logConnection->commit(); // 提交日志事务,立即保存 } catch (\Exception $e) { $logConnection->rollBack(); // 可选:处理日志保存失败的情况,比如写入文件日志 \Log::error('API日志保存失败: ' . $e->getMessage()); } // 返回API响应状态 return (object)[ 'status' => $apiResponse['status'] ?? false, 'response' => $apiResponse ]; } }
原理说明
- 主业务操作使用默认数据库连接,在主事务中执行,失败时回滚所有业务SQL。
- 日志操作使用独立的数据库连接,拥有自己的事务上下文,提交后立即写入数据库,不受主事务回滚的影响。
- 如果不想新增数据库连接,也可以直接使用原生PDO执行日志插入(绕过Laravel的事务管理),但使用独立连接的方式更符合Laravel的编码规范。
内容的提问来源于stack exchange,提问作者Miumicream
相关产品推荐
相关产品推荐

