実現したいこと
PHP の PDO で beginTransaction()
を入れ子にしたいです。
発生している問題・エラーメッセージ
"There is no active transaction" という PDOException になってしまいます。
該当のソースコード
下記 insert()
メソッドの実行でエラーになります。
(下記は単一テーブルへのINSERTなのでトランザクションを開く意味がないのですが、実際は複数テーブルへのINSERTを行っています。質問用に「入れ子のトランザクションが出来ないこと」を簡易に示すためのコードとして単一テーブルにしています。)
php
1 // 入れ子のトランザクションで INSERT を実行2 final public function insert(): string3 {4 try {5 // 外側のトランザクション開始6 $this->pdo->beginTransaction();7 8 // test_table1 の INSERT を実行9 $stmt1 = $this->pdo->prepare("INSERT INTO test_table1 (column1) VALUES (:value1)");10 $value1 = 'some_value_1';11 $stmt1->bindParam(':value1', $value1);12 $stmt1->execute();13 14 try {15 // 入れ子のトランザクション開始16 $this->pdo->beginTransaction();17 18 // test_table2 の INSERT を実行19 $stmt2 = $this->pdo->prepare("INSERT INTO test_table2 (column2) VALUES (:value2)");20 $value2 = 'some_value_2';21 $stmt2->bindParam(':value2', $value2);22 $stmt2->execute();23 24 // 入れ子のトランザクションのコミット25 $this->pdo->commit();26 } catch (\Exception $e) {27 // 入れ子のトランザクションのロールバック28 $this->pdo->rollBack();29 throw $e;30 }31 32 // 外側のトランザクションのコミット33 $this->pdo->commit();34 35 return "成功";36 } catch (\Exception $e) {37 // 外側のトランザクションのロールバック38 $this->pdo->rollBack(); // この行でエラーが発生39 return "エラー: " . $e->getMessage();40 }41 }
再現可能なコード全体をこちらのリンクに置いておきました。よろしければお試し頂けますと幸いです。( 21~23 行目のデータベース接続情報だけ変更してください。)
https://3v4l.org/HivEd
試したこと
トランザクションを管理しているデータベース接続が同じだからかな?と思い上記の$this->pdo
をやめて、__construct()
で $this->pdo1
と $this->pdo2
を開き、外側を$this->pdo1
に、入れ子を$this->pdo2
に、と分けてみましたが変化は見らせませんでした。
ツールのバージョン
PHP は 8.2 です。
MySQL はやや古めで 5.7 です。
よろしくお願い致します。
追記
改めて 5.7 のマニュアルを読んでみましたら 「> Transactions cannot be nested. 」との記載がございました…。
https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html
こういう場合、どのように対処したらよろしいでしょうか?
入れ子の処理の方をinsert()
とは別の insert2()
と分離しておき、その引数に「トランザクションを開くか否か」としてinsert2($withTransaction)
のように対処することになるでしょうか? ( insert2()
を入れ子として実行するときは $withTransaction
を false
にし、単一で実行するときは true
にするという意味です。 )
これでいいのかと、経験が浅く不安が拭えません。
もし妙案ございましたらよろしくお願い致します。
0 コメント