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

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

(1.5)の続き

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

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

ConnectionManager のbeginを利用してトランザクションを開始し、execute()メソッドを使用

ordersに1レコード挿入後、再度トランザクションを開始し、order_detailsに2レコード挿入する

    protected function nestExecuteSuccess() : void {
        $this->io->info("ネストしたトランザクションのテスト。\n最初にtruncate table 実行する。");
        $this->io->hr();
        /** @var Cake\Database\Connection */
        $connection = ConnectionManager::get('default');

        $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->commit();
                $successOrderDetail = 'success';
            }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);
    }
[dev]$ bin/cake transaction nestExecuteSuccess
ネストしたトランザクションのテスト。
最初にtruncate table 実行する。
-------------------------------------------------------------------------------
ordersに挿入(成功SQL実行)
order_detailsに挿入(成功SQL実行)
-------------------------------------------------------------------------------
Ordersのレコード数:1
OrderDetailsのレコード数:2
$successOrder = success
$successOrderDetail = success

CakePHPのqueryログ。

debug: connection= role=write duration=5.4 rows=0 truncate table orders;
debug: connection= role=write duration=52 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.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= duration=0 rows=0 COMMIT

結果:トランザクションがネストしていない。ただし生のSQLと異なり、2回目のbeginは強制commitされず無視される。
savepointが使われていない。

さらに、ordersへの挿入をorder_detailsの後ろに移動してみる。

        $connection->begin();
        $successOrder = 'fail';
        $successOrderDetail = 'fail';
        try{
            $this->io->out("ordersに挿入(成功SQL実行)");

            $connection->begin();
            try{
                $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->commit();
                $successOrderDetail = 'success';
            }catch(Exception $ex){
                $this->io->error('Exception in orderDetails 発生');
                $this->io->error($ex->getMessage());
                $connection->rollback();
            }
            $this->io->out("order_detailsに挿入(成功SQL実行)");
            $ret = $connection->execute(
                'INSERT into orders (order_name) values(?)',
                ["name1"]
            );

            $connection->commit();
            $successOrder = 'success';
        }catch(Exception $ex){
            $this->io->error('Exception in orders 発生');
            $this->io->error($ex->getMessage());
            $connection->rollback();
        }finally{
        }
[dev]$ bin/cake transaction nestExecuteSuccess
ネストしたトランザクションのテスト。
最初にtruncate table 実行する。
-------------------------------------------------------------------------------
order_detailsに挿入(成功SQL実行)
ordersに挿入(成功SQL実行)
-------------------------------------------------------------------------------
Ordersのレコード数:1
OrderDetailsのレコード数:2
$successOrder = success
$successOrderDetail = success
debug: connection= role= duration=0 rows=0 BEGIN
debug: connection= role=write duration=0.2 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) debug: connection= role=write duration=0 rows=1 INSERT into orders (order_name) values('name1')
debug: connection= role= duration=0 rows=0 COMMIT

結果:内側のcommitも無視されていることがわかる。
savepointが使われていない。→ 別記事にするが、意図的に有効にしなければ効かない。

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

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

        $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);

    }
[dev]$ bin/cake transaction nestExecuteRollback
ネストしたトランザクションのテスト。
最初にtruncate table 実行し、子トランザクション内でrollback()を実行。
-------------------------------------------------------------------------------
ordersに挿入(成功SQL実行)
order_detailsに挿入(成功SQL実行)
-------------------------------------------------------------------------------
Ordersのレコード数:0
OrderDetailsのレコード数:0
$successOrder = success
$successOrderDetail = force rollback
debug: connection= role= duration=0 rows=0 BEGIN
debug: connection= role=write duration=1.8 rows=1 INSERT into orders (order_name) values('name1')
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.1 rows=1 INSERT into order_details (order_id,sub_order_name) values('1','sub_name2')
debug: connection= role= duration=0 rows=0 ROLLBACK

結果:rollback() は、親のbegin() に対応してしまっている。

3へ続く。

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