(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() されている。