Propelで作ったモデルのsaveメソッドとトランザクション
個人用メモ&symfony1.0.17での話。
Propel+Creoleで明示的にトランザクションを開始・終了するには
<?php $con = Propel::getConnection(); try { $con->begin(); : $con->commit(); } catch (Exception $e) { $con->rollback(); throw $e; } }
な感じで、CreoleのConnection#begin、Connection#commit、Connection#rollbackを使えばOK(Connectionはインターフェース)。
で、Propelで作ったモデルのBaseクラスのsaveメソッドを見ると
<?php public function save($con = null) { : try { $con->begin(); $affectedRows = $this->doSave($con); $con->commit(); return $affectedRows; } catch (PropelException $e) { $con->rollback(); throw $e; } } :
のように、ここでトランザクションをcommit/rollbackしているように見える(かなり焦った)。ここでコードを追ってみると、抽象クラスであるConnectionCommonクラスでbegin/commit/rollbackの実装がされている。で、
<?php abstract class ConnectionCommon { : public function commit() { if ($this->transactionOpcount > 0) { if ($this->transactionOpcount == 1 || $this->supportsNestedTrans()) { $this->commitTrans(); } $this->transactionOpcount--; } }
な感じで、トランザクションのネストとネストしたトランザクションをサポートしてるかどうかをチェックしている。今使っているのはPostgreSQL8.3系でネストしたトランザクションはサポートされていないはずで、実際に使うPgSQLConnectionクラス(ConnectionCommonクラスのサブクラス)でもsupportsNestedTransメソッドでfalseが返るようになっている。
なので、
<?php class Relevancy extends BaseRelevancy { public function save($con = null) { $con = Propel::getConnection(); try { $con->begin(); $ret = parent::save($con); $answer = $this->getAnswer(); if ($this->getScore() == 1) { $answer->setRelevancyUp($answer->getRelevancyUp() + 1); } else { $answer->setRelevancyDown($answer->getRelevancyDown() + 1); } $answer->save($con); $con->commit(); return $ret; } catch (Exception $e) { $con->rollback(); throw $e; } } }
と書いてもフラット(?)な1つのトランザクションで管理される(ハズ)。