MariaDB とCakePHP5でのトランザクションの実験(3)

CakePHP
この記事は約8分で読めます。

(2)の続き

(本記事は書きかけで随時更新しております。)

ネストしたトランザクションの挙動を見ていく。
なお、MariaDBはネストしたトランザクション(BEGIN始まり)はサポートしていない。ネストにはSAVEPOINTを使用する。

CakePHP5.1では、Connection オブジェクトのexecute()メソッドを使った場合、ネストしたトランザクションはサポートされない。これは、SAVEPOINTをデフォルトで使用しない設定になっているためである。SAVEPOINTをサポートするデータベースの場合は、最初に設定が必要である。

        // $this->io は、ConsoleIo $io のコピー  
        /** @var Cake\Database\Connection */
        $connection = ConnectionManager::get('default');
        $status = $connection->isSavePointsEnabled();
        $this->io->info($status ? 'enable' : 'disable');  // disable を表示
        $connection->enableSavePoints(true);
        $status = $connection->isSavePointsEnabled();
        $this->io->info($status ? 'enable' : 'disable');  // enable を表示

これを指定すると、ネストしたトランザクションが有効となる。

※しかし、isSavePointsEnabled()とか、CakePHP BookではなくAPIにしか載っていない。PHPなのでClassのソースコードを見たり検索するのが早い。

savepointを有効にし、ordersに1レコード挿入後、再度トランザクションを開始し、order_detailsに2レコード挿入するが、子トランザクション内でrollback() する。

    protected function nestExecuteSavepointRollback() : void {
        $this->io->info("ネストしたトランザクションのテスト。SAVEPOINT ON \n最初にtruncate table 実行し、子トランザクション内でrollback()を実行。");
        $this->io->hr();
        /** @var Cake\Database\Connection */
        $connection = ConnectionManager::get('default');
        $status = $connection->isSavePointsEnabled();
        $connection->enableSavePoints(true);

        $ret = $connection->execute( 'truncate table orders;' );
        $ret = $connection->execute( 'truncate table order_details;' );
        $connection->begin();
        $successOrder = 'fail';
        $successOrderDetail = 'fail';
        try{
            $this->io->out("ordersに挿入(成功SQL実行)");
            $ret = $connection->execute(
                'INSERT into orders (order_name) values(?)',
                ["name1"]
            );

            $connection->begin();
            try{
                $this->io->out("order_detailsに挿入(成功SQL実行)");
                $ret = $connection->execute(
                    'INSERT into order_details (order_id,sub_order_name) values(?,?)',
                    [1,"sub_name1"]
                );
                $ret = $connection->execute(
                    'INSERT into order_details (order_id,sub_order_name) values(?,?)',
                    [1,"sub_name2"]
                );
                $connection->rollback();
                $successOrderDetail = 'force rollback';
            }catch(Exception $ex){
                $this->io->error('Exception in orderDetails 発生');
                $this->io->error($ex->getMessage());
                $connection->rollback();
            }
            $connection->commit();
            $successOrder = 'success';
        }catch(Exception $ex){
            $this->io->error('Exception in orders 発生');
            $this->io->error($ex->getMessage());
            $connection->rollback();
        }finally{
        }

        $this->io->hr();
        $ordersTable = $this->fetchTable('Orders');
        $orderRowCount = $ordersTable->find()->count();
        $orderDetailsTable = $this->fetchTable('OrderDetails');
        $orderDetailRowCount = $orderDetailsTable->find()->count();
        $this->io->warning('Ordersのレコード数:'.$orderRowCount);
        $this->io->warning('OrderDetailsのレコード数:'.$orderDetailRowCount);

        $this->io->warning('$successOrder = '.$successOrder);
        $this->io->warning('$successOrderDetail = '.$successOrderDetail);

    }
debug: connection= role=write duration=5.3 rows=0 truncate table orders;
debug: connection= role=write duration=4.4 rows=0 truncate table order_details;
debug: connection= role= duration=0 rows=0 BEGIN
debug: connection= role=write duration=0.3 rows=1 INSERT into orders (order_name) values('name1')
debug: connection= role=write duration=0 rows=0 SAVEPOINT LEVEL1
debug: connection= role=write duration=0.1 rows=1 INSERT into order_details (order_id,sub_order_name) values('1','sub_name1')
debug: connection= role=write duration=0 rows=1 INSERT into order_details (order_id,sub_order_name) values('1','sub_name2')
debug: connection= role=write duration=0.9 rows=0 ROLLBACK TO SAVEPOINT LEVEL1
debug: connection= role= duration=0 rows=0 COMMIT

結果:トランザクションはネストし、子のrollback() のみが効いており、親のトランザクションはcommit() されている。

(4) に続く。

タイトルとURLをコピーしました