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

CakePHP

(2)の続き

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

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

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// $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 を表示
// $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 を表示
        // $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() する。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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);
}
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); }
    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);

    }
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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) に続く。

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