コミット
ネストされたDB::commit()は無視され、最も浅いDB::commit()でコミットされます。
以下の例では、DBにユーザー2人が作成されるのは、t1()
のDB::commit()実行時です。
t2()
内のDB::commit()では、DBにユーザーは誰も作成されていません。
function t1()
{
DB::beginTransaction();
User::create(['id' => 1]);
t2();
sleep(10);
DB::commit(); // ここでコミットされる
}
function t2()
{
DB::beginTransaction();
User::create(['id' => 2]);
DB::commit(); // ここではコミットされない
}
t1();
sleep(10)実行中に、DBでselect * from users;
することで、コミットされていないことを確認できます。
Laravelはトランザクションのカウントを取っている
トランザクションのカウント($this->transactions
)を取り、そのカウントが1の時のみコミットを実行しているようです。
カウントはトランザクション開始するごとに増え、コミットするごとに減っています。
public function beginTransaction()
{
$this->createTransaction();
$this->transactions++;
$this->fireConnectionEvent('beganTransaction');
}
public function commit()
{
if ($this->transactions == 1) {
$this->getPdo()->commit();
}
$this->transactions = max(0, $this->transactions - 1);
$this->fireConnectionEvent('committed');
}
ちなみに2回目以降のトランザクション開始はセーブポイント作成になるようです。
参考:https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Concerns/ManagesTransactions.php
DB::commit()し忘れると
トランザクションのカウントはDB::beginTransaction()
するごとに増えていきますので、1つ忘れると全てのトランザクションがコミットされません。
シンプルな例ですが、以下はDBにユーザーは誰も作成されません。
DB::beginTransaction();
User::create(['id' => 1]);
// DB::commit(); // コミット忘れた
DB::beginTransaction();
User::create(['id' => 2]);
DB::commit();
ロールバック
DB::rollBack()
した時もトランザクションのカウントが1つ減ります。また2回目以降のDB::beginTransaction()
はセーブポイント作成になります。
よって以下の例は、id1のユーザーのみ作成されます。
DB::beginTransaction();
User::create(['id' => 1]);
DB::beginTransaction();
User::create(['id' => 2]);
DB::rollBack();
DB::commit();
以下の例では、id1のユーザーのみDBに作成されます。
t2()
で例外がスローされていますが握りつぶしているため、t2()
はロールバック、t1()
はコミットという結果になっています。
function t1()
{
try {
DB::beginTransaction();
User::create(['id' => 1]);
t2();
DB::commit();
} catch (Exception $e) {
DB::rollBack();
}
}
function t2()
{
try {
DB::beginTransaction();
User::create(['id' => 2]);
throw new Exception; // 例外発生
DB::commit();
} catch (Exception $e) {
DB::rollBack();
// ここでthrow $eせず、例外を握りつぶしている
}
}
t1();
例外を握り潰さないようにすると、全てロールバックされます。
// id1もid2も作成されない
\DB::transaction(function () {
User::create(['id' => 1]);
\DB::transaction(function () {
User::create(['id' => 2]);
throw new Exception;
});
});