PhalconでDIへDb\Adapterを登録してtransactionを一元管理するのはできないのだと思っていたら 使い方が間違っていた。
DIへ登録する時にはshared(共有)として登録すれば呼び出し先で同じコネクションのインスタンス を使用してくれる。逆を言うとsharedしないと同じインスタンスを使用してくれないようだ。
具体的には
// 第三引数にtrueを指定 $di->set('db', function() use ($config) { return new \Phalcon\Db\Adapter\Pdo\Postgresql( $config->database->toArray()); }, true);本当にトランザクションが制御できているか以下の方法で確認
DBにテーブルを用意する
CREATE TABLE name1 ( id SERIAL PRIMARY KEY, name text );テスト登録用処理を実装したモデルクラスを用意する。
class Name1 extends \Phalcon\MVC\Model { public $id; public $name; public function getSource() { return "name1"; } /** * 引数のコネクションと関数内で生成したコネクション * でそれぞれ登録 * */ public static function insertTest($src_db){ $name = new \App\Mvc\Model\Name1(); $db = $name->getDI()->get("db"); for($i=0;$i<5;$i++){ $name = new \App\Mvc\Model\Name1(); $name->name = 'FFFFF' . sprintf("%05d", $i); $name->create(); $result = $db->query("INSERT INTO name1(name) VALUES( '" . "DDDDD" . sprintf("%05d", $i) . "')"); $resulta = $src_db->query("INSERT INTO name1(name) VALUES( '" . "EEEEE" . sprintf("%05d", $i) . "')"); } } }DIのsharedをtrueにしたものとしないものの両方で以下の処理を実行する。
具体的にはコントローラー、生成したモデルのテスト用関数で登録処理が行われ、例外を意図的に 発生させてrollbackを行う。
try{ $this->db->begin(); for($i=0;$i<5;$i++){ $name = new \App\Mvc\Model\Name1(); $name->name = 'AAAAAA' . sprintf("%05d", $i); $name->create(); $result = $this->db->query("INSERT INTO name1(name) VALUES( '" . "BBBBB" . sprintf("%05d", $i) . "')"); } \App\Mvc\Model\Name1::insertTest($this->db); throw new \Exception("trnsaction test"); $this->db->commit(); }catch(Exception $e){ if($this->db->isUnderTransaction()){ $this->db->rollback(); } throw $e; }sharedした場合の結果は
test=# select * from name1; id | name ----+------ (0 行)しなかった場合、インスタンスを別で取得して登録した分が残ってしまう。
test=# select * from name1; id | name ----+------------ 12 | DDDDD00000 15 | DDDDD00001 18 | DDDDD00002 21 | DDDDD00003 24 | DDDDD00004 (5 行)一元管理したい場合はsharedをする事で可能になる。一番起きそうなのは一元管理するつもりだったのに sharedしないで登録してしまいtransactionがめちゃくちゃになってしまったという事故。
connectionのインスタンスはDB回りのクラスで管理されるものと思い込んでいたのでDIでやる発想が無かった。 こんな方法もあるんだな。
0 件のコメント :
コメントを投稿