4
5

More than 5 years have passed since last update.

CakePHP 3 でトランザクションを入れ子にできるか?

Last updated at Posted at 2017-07-30

CakePHP3で開発をしていて、PHPUnitで少し複雑なテストをしようとごにょごにょやってたら、トランザクションをネストしてロールバックしたくなったので調べました。

SQLにおけるトランザクションはネストできるか?

本記事でいうトランザクションとはSQLのアレです。コレです。ではそもそもトランザクションとはネストできるものなのかというと、通常はできません。詳しくは下記の記事に詳しく書いてありました。

トランザクションをネストしたらどうなる? 内側だけロールバックできる? - Qiita

記事によると、TRANSACTION命令だけでなく、SAVEPOINTを使えば部分的ロールバックが可能だということです。CakePHP3ではどうすればよいのでしょうか。

結論:CakePHP3ではトランザクションをネストできる

結論から先に言うと、CakePHP3では普通にトランザクションをネストできます。内部的には前出のSAVEPOINTを利用して実現されています。詳細はcakephp/src/Database/Connection.phpのbegin()メソッドのソースを見ればすぐわかります。CakePHP 3.4時点でのコードを引用してみます。

cakephp/src/Database/Connection.php
    /**
     * Starts a new transaction.
     *
     * @return void
     */
    public function begin()
    {
        if (!$this->_transactionStarted) {
            if ($this->_logQueries) {
                $this->log('BEGIN');
            }
            $this->_driver->beginTransaction();
            $this->_transactionLevel = 0;
            $this->_transactionStarted = true;
            $this->nestedTransactionRollbackException = null;
            return;
        }
        $this->_transactionLevel++;
        if ($this->isSavePointsEnabled()) {
            $this->createSavePoint($this->_transactionLevel);
        }
    }

こんな感じで、begin()メソッドをネストして使うと最初のif文の!$this->_transactionStartedがfalseになるためSAVEPOINTの作成処理が実行されます。部分的ロールバック等も普通にできます。

注意点としては、$this->isSavePointsEnabled()でfalseが返ってくる場合はネストできません(少し前のバージョンだと$this->useSavePoints()だったりします)。こちらはドライバの実装に依ると書かれていますが、今のところMySQL, Postgres, SQLite, SQL Serverの全てで利用可能であるようです。

というわけで、基本的には何も考えずbegin()を複数回ネストしても大丈夫です。Cake側でよしなにやってくれます。Cake万歳。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5