(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() に対応してしまっている。