フレームワークなしでPHPの1つのプロジェクトに新しい機能を追加する場合、トランザクションを使用する必要が生じました(MySQLをInnoDBで、PHP 5.4をMYSQLiで使用)。
プロジェクトでは、
autocommit
デフォルトで
true
設定されています。 プロジェクト全体でオフにすることはできません。 したがって、最初の考えは、SQLクエリを実行する前に
autocommit
をオフにし、すべてのアクション(および最後に
commit
または
rollback
)の後、
autocommit
再びオンにすることでした。
しかし、このアプローチはすぐに受け入れられないことが判明しました。これは、通常、リクエストが行われる複数のメソッドを連続して実行する必要があり、メソッドの1つで例外が発生した場合、
rollback
です 各メソッドで
commit
、すべてのリクエストが実行される前に変更がコミットされます。
別のオプションは、関連するメソッドの各グループが完了した後に
autocommit
を無効および有効にすることです。 条件付きコード(アクションはクラスで実行されます):
public function save() { $result = $this->db->update(...); // - , . if (!$result) throw new Exception('Error while saving'); } public function append_log() { $result = $this->db->insert(...); if (!$result) throw new Exception('Error while append'); } public function add() { $this->db->autocommit(false); try { $this->save(); $this->append_log(); $this->db->commit(); } catch (Exception $e) { $this->db->rollback(); } $this->db->autocommit(true); }
しかし、ここでは2つの問題が発生します。
- 私は本当に各方法でこれを書きたくありません
- メソッド(
save()
またはappend_log()
)のいずれかが、トランザクションに結合する必要のある複数の連続したリクエストもappend_log()
どうなりますか? 次に、commit
を実行commit
と親の変更も保存されるため、autocommit
かどうかを判断し、これに応じてcommit
を実行commit
必要があります。
変更を確認および修正するためのコードが、参加せずにメソッドの周囲で実行されることを確認する必要があります。
public function transaction(callable $block) { $exception = null; if ($need_to_off = $this->isAutocommitOn()) $this->mysqli->autocommit(false); try { $block(); } catch (Exception $e) { $exception = $e; } if ($need_to_off) { if ($exception == null) { $this->db->mysqli->commit(); } else { $this->db->mysqli->rollback(); } $this->mysqli->autocommit(true); } if ($exception) throw $exception; } public function isAutocommitOn() { if ($result = $this->db->mysqli->query("SELECT @@autocommit")) { $row = $result->fetch_row(); $result->free(); } return isset($row[0]) && $row[0] == 1; }
無名関数内で
transaction()
メソッドを送信します。
autocommit
有効になっている場合、
transaction
はそれを無効にしてから、匿名機能を実行します。 結果に応じて、
commit
または
rollback
を行い、
autocommit
再度有効にします。
autocommit
すでにオフになっている場合、匿名関数が実行されるだけです-自動コミットは他の場所で処理されます。
使用例:
public function save_all() { $this->transaction(function(){ $this->save(); $this->append_log(); }); }
PS:クロージャーの
$this
は、PHPバージョン5.4以降で使用できます。