毎度同じことをやろうと思った時にどうやればいいか忘れてしまうので備忘も兼ねて
はじめに
Laravelでは以下のコードのようにするとトランザクションをネストしてSAVEPOINTを作成するため、やんごとなき事情で新しいトランザクションを切りたいなーと思ってもうまくいきません。
(ID発番が連番ではない場合に別テーブルでID発番を管理しトランザクションを分けることでロックする時間を短くするとか)
DB::beginTransaction();
{
DB::beginTransaction();
Animal::create(['name' => 'ぺんぎんさん']);
DB::commit();
}
DB::rollBack(); // ぺんぎんさんの挿入もロールバックされる!
SAVEPOINTってなんだ!って方は以下の記事が非常にわかりやすかったです。
●トランザクションのネストの使い方まとめた(初心者向け)
https://qiita.com/_natsu_no_yuki_/items/e1db2a132cbff740896d
実装方法
トランザクションを乱立させることはそう多くないと思うので、簡単にできる別コネクションを作成する方法を記載します。
準備
config/database.php
にデフォルトで利用している接続情報をまるっとコピーします。
// 既存の接続情報
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'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', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
// ★mysqlの項目を丸コピした接続情報
'mysql_2' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'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', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
実装
新しいトランザクションを切りたい場合は、DBファサードのconnection
で追加した接続情報のキーを設定します。
Eloquentを利用する場合は、その中でModelのインスタンスを作成し、setConnection
で追加した接続情報のキーを設定します。
- 手動トランザクションの場合
// デフォルトのコネクションでトランザクションの開始
DB::beginTransaction();
{
// 追加した接続情報をセットして、トランザクションの開始
$mysql2 = \DB::connection('mysql_2');
$mysql2->beginTransaction();
// Modelのインスタンスを作成し、追加した接続情報をセットする
$animal = new Animal();
$animal->setConnection('mysql_2');
$animal->create(['name' => 'ぺんぎんさん']);
// mysql_2のコネクションをコミットする
$mysql2->commit();
}
// デフォルトのコネクションをロールバックする
DB::rollBack(); // ぺんぎんさんのデータもコミットされている!
- トランザクションメソッドの場合
DB::transaction(function () {
DB::connection('mysql_2')->transaction(function () {
$animal = new Animal();
$animal->setConnection('mysql_2');
$animal->create(['name' => 'ぺんぎんさん']);
});
throw new Exception(); // ぺんぎんさんのデータもコミットされている!
});
おわりに
なんだかんだネストではなく、別トランザクションを切りたくなることはあるので、newTransaction
とかできると嬉しいんだろうなというお気持ちでした。