フレキシブルにお客様のご要望にあわせ提供します.

PHP PDOクラスを用いたトランザクションの実際

 

序文

テーブルが正規化の思想で設計されてゐる実際のデータベースにアクセスするユーザプログラムに於いては  データの登録処理では 必ず 複数のテーブルに対して連続して書き込むやうな処理を実装することに成る。その一連の書き込みの途中で 他のセッションからの干渉が入らぬやうにするのが「排他」であるがここでは取り扱わない。

 

一方で  ユーザプログラムの実装に何らかの不手際があり、一連の書き込みの途中で SQL Error を起こしてしまう可能性もある。その場合、たんに終了させてしまつた場合、中断までに書き込んでしまつたデータだけが残ると、データベースでデータの不整合が起きる。

 

勿論、実用上は そもゝそのやうな不整合を起す bug があつてはならないし有れば使い物に成らないプログラムだが万が一有った場合、その不整合はデータベースの破綻を意味し、場合によってはそれは財産の破壊と等価である。それを確実に防がねばならない。

 

そのためには一連の処理が完了しなかつた場合は書き込みをしないというやうな仕組みが必要である。

 

若し不整合に関して、それを自己の実装で解決しやうとすれば、都度の書き込みを記憶していて問題が発生したらあとから削除するといふ(Read and modify の)やうな発想が有ると思ふが、それは不整合の修正であつて発生の防止ではない。

そのやうな面倒で 間違いの起きる方法を採らずとも SQL データベースにはそもゝを起こさない「トランザクション」といふ仕組みがあり、PHP など多数の言語からデータベースにその機能を実行させることができる。

 

 

beginTransaction(),  rollBack(),  comit()

PHP から SQL のトランザクション機能を利用するには PHP 標準の PDO classの上記 3関数を利用する。また  inTransaction() といふ transaction 中か否かを返す関数は有るが必須ではない。

 

 

例外と組み合はせて

PHP PDO では適切に コンストラクタを呼び出してゐれば  インスタンスで何も設定せずとも SQL エラー時に例外が発生する。例外発生で受け取る変数は PDOException 型ではあるが  Exception 型の指定であつても受け取れる。初期状態は いかなる SQL Error code でも検出され次第、例外が発生する。setAttribute による例外にまつわる明示的な設定は初期パラメータ以外を設定する場合もしくは一度設定したものを戻す場合におこなう。

 

トランザクションを利用するのに例外構文は必須ではないが rollBack(),  comit()  の実行タイミングを考える上では例外構文を利用した方が 簡素に成るし寧ろそうすべきである。

 

 

例外が呼び出されない場合

SQL Error の例外が呼び出されない場合、コンストラクタが不適当である。おそらく PDO class を直接ではなく何らかの独自 class を経由して呼び出した場合、その 独自 class 内での実装に不手際があり、メインメソッドでインスタンス生成されたときに PDO class のコンストラクタが呼び出されてゐない pattern があると思はれる。

独自 class は  PDO class を  extends  し、 そのコンストラクタ内で  継承した親たる PDO の コンストラクタを呼び出すといふ  Object oriented programming に於ける class 構文仕様の基礎に沿って実装するとよいだらう。

 

 

コード (ユーザクラス内のコンストラクタ)


class UserDBClass extends PDO {
function __construct() { 
parent::__construct(" ## new PDO()でインスタンス生成するときの引数と同じ内容 ### "); 

}  //コンストラクタ

}

 

ユーザクラスのコンストラクタをこのやうに実装しておくだけで呼び出し側では

 


$dbh =new UserDBClass();

 

これだけでよいのだ。勿論これは 独自 class 経由の場合であつて、直接に  new PDO( データベース接続設定)  で呼び出してもよい。なほ Java や C# ではコントラクタは class と同じ名称だが PHP では固有の名称であり、 _constract() がコンストラクタである。

 

 

さて本題に入る。以下に連続して3つテーブルに対して書き込む場合のトランザクションの例について示す。

 

 

トランザクション


$dbh =new UserDBClass();  // UserDBClass に就いては前述のとおり

$dbh->beginTransaction();

try{

$sql = ""; $sth = $dbh->prepare($sql); $sth->execute();
$sql = ""; $sth = $dbh->prepare($sql); $sth->execute();
$sql = ""; $sth = $dbh->prepare($sql); $sth->execute();

$dbh->commit();

}
catch(PDOException $dberr){$dbh->rollBack(); echo $dberr}

 

たつたこれだけである。SQL エラーが出た場合, その段階で例外に飛び rollBack が実行される。どこで止まったか知りたいなら catch で受け取る変数 $dberr  (変数名は任意)  を echo 出力すれば停止した PHPの行も console 文に含まれてゐる。

 

さらに若し 停止した場所により処理分けをしたい場合は、たんにフラグを立てておけばよい。


$dbh =new UserDBClass();  // UserDBClass に就いては前述のとおり

$dbh->beginTransaction();
$flag=null;

try{

$sql = ""; $sth = $dbh->prepare($sql); $sth->execute();
flag[0]=1;
$sql = ""; $sth = $dbh->prepare($sql); $sth->execute();
flag[1]=1;
$sql = ""; $sth = $dbh->prepare($sql); $sth->execute();
flag[2]=1;

$dbh->commit();

}
catch(PDOException $dberr){$dbh->rollBack(); }

//止まった位置により、なんらかの場合分け処理したい場合
if($flag[0]==1) {  ~~}

$flag=null;


rollBack は catch 内に直接書いておいたほうがよく、若し更に停止位置に拠る場合分けの処理をさせたい場合はその後におこなへばよいだらう。

 

安心禁物

さてトランザクションはこれを base に実装していけばよろしいが、そもそもこれは非常停止ボタンみたいな存在(←たとえがわるい)であつて、いわゆる機能ではなひ。但し、まづは本質的ではないツマラヌものから、つまりトランザクションやテスト用コードから書いていったほうが、事後はよろしいかもしれなひ。