Edited at

Laravel5.5で同一接続先ホストでの複数DBトランザクションを行う方法


背景

laravel5.5で複数DBでのトランザクションをする方法が調べてもなかなか見つからなかったので書くことにしました。


別コネクションを作る方法

これはlaravelで複数DBに接続させる方法で調べるといろいろ記事が出てきます。(2018/12/01)

Laravelで別々のDB(MySQL)に接続させる方法

設定を.envでやりたい場合は追加します。

DB_CONNECTION2=mysql2

DB_HOST2=localhost
DB_PORT2=3306
DB_DATABASE2=sample2
DB_USERNAME2=sample2
DB_PASSWORD2=sample2

その上でconfig/database.phpにその設定を追加します。


config/database.php

        'mysql2' => [

'driver' => 'mysql',
'host' => env('DB_HOST2', '127.0.0.1'),
'port' => env('DB_PORT2', '3306'),
'database' => env('DB_DATABASE2', 'forge'),
'username' => env('DB_USERNAME2', 'forge'),
'password' => env('DB_PASSWORD2', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],

Eloquentにはdatabase.phpで設定したmysql2をconnectionに記述します


app/Test2.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Test2 extends Model
{
protected $connection = 'mysql2';
protected $guarded = ['id'];
}


クエリビルダーはコネクションから指定します。

DB::connection('mysql2')->select('select * from test2s');


別コネクションの問題点

実際にDBが違うホストにあるならコネクションが別れるのは当然だと思いますが、同一ホストにある場合はコネクションを分けたくなかったりします。

実際トランザクションに対して以下の記事を見つけました。

Multiple DB Connections in Laravel


Be careful about attempting to build relationships with tables across databases! It is possible to do, but it can come with some caveats and depends on what database and/or database settings you have.


複数のデータベースを跨いだリレーションを組むことには注意が必要です。いくつかの注意点があり、それぞれの環境により注意しなければいけません。



「いくつかの注意点」についてこの記事には書かれていません。

じゃあどんな注意点があるんだというと以下。

LaravelのDBトランザクション落とし穴

つまり、それぞれ違うコネクションを設定したテーブル(クエリビルダーもEloquentも関係なし)で、トランザクションした場合、デフォルトのコネクションの方しかロールバックされないということ。試してみると確かに片方しかロールバックされませんでした。

そして、これまたじゃあどうすりゃいいんだってことは書かれていません。

なので自分が試して上手くいった方法について載せておきます。


1コネクションでやる方法

.envにもconfig/database.phpにも設定を追加する必要はありません。

Eloquentにはconnectionを書かない代わりにtableにDB名から書きます。


app/Test2.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Test2 extends Model
{
// sample2は別DBの名前です
protected $table = 'sample2.test2s';
protected $guarded = ['id'];
}


クエリビルダーもテーブル名にDB名付きで入れればOKです。

DB::table('sample2.test2s')->get();

実際にmysqlでprocesslistを見れば1つしかプロセスがないことが分かると思います。

mysql> show processlist;

これでLaravelのDBトランザクション落とし穴の記事のようにDB::transactionをやったとき、デフォルトのテーブルしかロールバックされないということはなく、2テーブルともロールバックされます。

あくまで接続先ホストが同じだった場合のやり方になりますので、違う場合はそもそもトランザクションを分けろということなんでしょうか。ご参考までに。