在 Laravel 6 的多个数据库连接(MySQL)中使用事务
1、现在一个 API,需要插入 4 张表,第 1 张表为数据库 A 中的表,其他 3 张表为数据库 B 中的表,且第 1 张表的主键会插入至第 2 张表中。已经配置了 2 个数据库连接。分别为 wordpress、db。在一个应用程序中建议仅连接一个数据库,但是现在是历史遗留问题。
2、现有的代码实现如下。仅在数据库 B 中的 3 张表的插入时使用了数据库事务。如果事务回滚,则数据库 A 中的表的已插入数据变为了脏数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | $wpTheme = Theme::create([ 'name' => $name , 'custom_name' => $custom_name , 'content' => $content , 'is_original' => $isOriginal , 'original_theme_name' => $config [ 'name' ], ]); DB::beginTransaction(); try { $themeInstallation = new ThemeInstallation(); $themeInstallation ->theme_store_theme_id = $saasThemeConfig [ $name ][ 'id' ]; $themeInstallation ->theme_id = $name ; $themeInstallation ->wp_theme_id = $wpTheme ->id; $themeInstallation ->theme_custom_name = $custom_name ; $themeInstallation ->save(); $themeInstallationVersion = new ThemeInstallationVersion(); $themeInstallationVersion ->theme_installation_id = $themeInstallation ->id; $themeInstallationVersion ->theme_store_theme_id = $saasThemeConfig [ $name ][ 'id' ]; $themeInstallationVersion ->theme_store_theme_version_id = $saasThemeConfig [ $name ][ 'id' ]; $themeInstallationVersion ->save(); $themeInstallationTask = new ThemeInstallationTask(); $themeInstallationTask ->theme_installation_id = $themeInstallation ->id; $themeInstallationTask ->theme_installation_version_id = $themeInstallationVersion ->id; $themeInstallationTask ->save(); DB::commit(); } catch (\Throwable $e ) { DB::rollBack(); throw $e ; } |
3、第 1 张表所对应的模型配置连接如下
1 2 3 4 | class Theme extends Model { protected $connection = 'wordpress' ; } |
4、第 2、3、4 张表所对应的模型配置连接为默认连接。
5、参考实现:https://laracasts.com/discuss/channels/laravel/transactions-with-multiple-database-connections 。不过,如果 DB::commit() 成功而 DB::connection(‘wordpress’)->commit() 失败会怎样? 在这种情况下,我们无法在 db 中回滚,并且一致性被破坏。
6、先测试事务成功的情形,确认 4 张表皆插入数据成功。
7、当第 1 张表(对应连接 wordpress、对应 $wpTheme)插入成功后,第 2 张表(对应连接 db)中插入失败,事务回滚,所有表中皆未插入数据。符合预期。如图1
1 2 3 4 5 6 7 8 9 10 11 12 | { "message": "SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'theme' for key 'theme_installation_theme_id_unique' (SQL: insert into `theme_installation` (`theme_custom_name`, `role`, `processing`, `processing_failed`, `theme_store_theme_id`, `theme_id`, `wp_theme_id`, `updated_at`, `created_at`) values (theme, unpublished, 1, 0, 9, theme, 31, 2022-05-18 10:39:55, 2022-05-18 10:39:55))", "code": "23000", "status_code": 500, "debug": { "line": 669, "file": "\\.\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Connection.php", "class": "Illuminate\\Database\\QueryException", "trace": { } } } |
8、当第 1 张表(对应连接 wordpress、对应 $wpTheme)插入成功后,第 2 张表(对应连接 db)中插入成功,第 3 张表(对应连接 db)中插入失败,事务回滚,所有表中皆未插入数据。符合预期。如图2
1 2 3 4 5 6 7 8 9 10 11 12 | { "message": "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'theme_installation_ide' in 'field list' (SQL: insert into `theme_installation_version` (`update_state`, `processing`, `processing_failed`, `theme_installation_ide`, `theme_store_theme_id`, `theme_store_theme_version_id`, `updated_at`, `created_at`) values (manual_update, 1, 0, 7, 9, 9, 2022-05-18 10:46:32, 2022-05-18 10:46:32))", "code": "42S22", "status_code": 500, "debug": { "line": 669, "file": "\\.\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Connection.php", "class": "Illuminate\\Database\\QueryException", "trace": { } } } |
9、当第 1 张表(对应连接 wordpress、对应 $wpTheme)插入失败后,所有表中皆未插入数据。符合预期。如图3
1 2 3 4 5 6 7 8 9 10 11 | { "message": "Array to string conversion", "status_code": 500, "debug": { "line": 419, "file": "\\.\\vendor\\laravel\\framework\\src\\Illuminate\\Support\\Str.php", "class": "ErrorException", "trace": { } } } |
10、针对问题,如果 DB::commit() 成功而 DB::connection(‘wordpress’)->commit() 失败会怎样?暂时不好模拟,决定留待后续分析处理。
近期评论