PEAR MDB2 は、デフォルトですべてのクエリについて自動コミットを行います。 しかし、 beginTransaction() を使用することで新たなトランザクションを開始し、それを commit() および rollback() で終了させることも可能です。 これらの 3 つのメソッドは、すべてオプションでセーブポイント文字列を指定することが可能です。 これを使用することで、対応する場所での開放やロールバックが可能となります。 inTransaction() メソッドを使用すると、 現在トランザクションがオープンしているのかどうかを調べることができます。
トランザクションの実行
<?php
// まずプログラムの最初に、$mdb2 という名前の
// MDB2 オブジェクトを作成します
require_once 'MDB2.php';
$mdb2 =& MDB2::connect('pgsql://usr:pw@localhost/dbnam');
if (PEAR::isError($mdb2)) {
die($mdb2->getMessage());
}
// このドライバでトランザクションがサポートされているかどうかを調べます
if (!$mdb2->supports('transactions')) {
exit();
}
// トランザクションをオープンします
$res = $mdb2->beginTransaction();
..
// トランザクション処理中かどうか、そして
// セーブポイントがサポートされているかどうかを調べます
if ($mdb2->inTransaction() && $mdb2->supports('savepoints')) {
// セーブポイントを設定します
$savepoint = 'MYSAVEPOINT';
$res = $mdb2->beginTransaction($savepoint);
..
// セーブポイントを開放するか、そこまでロールバックするかを判断します
if ($error_condition) {
$res = $mdb2->rollback($savepoint);
} else {
$res = $mdb2->commit($savepoint);
}
}
..
// コミットするかロールバックするかを判断します
if ($error_condition) {
$res = $mdb2->rollback();
} else {
$res = $mdb2->commit();
}
?>
PEAR MDB2 は、トランザクションやセーブポイントの エミュレートは行いません。つまり、MDB2 でトランザクションが使用できるといっても それはバックエンドの RDBMS (そして MySQL の場合は 使用するストレージエンジン) に依存するということです。 また、一部の RDBMS では DDL 文を実行すると 暗黙的にトランザクションがコミットされてしまうことにも注意しましょう - 明白な例外は Oracle および PostgreSQL です。
MDB2 は "入れ子状の" トランザクションもサポートしています。これを利用するには beginNestedTransaction() メソッドを使用します。 実際のところこれは、例えば Interbase でネイティブにサポートされているような 真の入れ子状トランザクションではありません。 MDB2 は、入れ子のトランザクションが何件オープンしているのかのカウンタを内部で管理します。 completeNestedTransaction() のコールによってトランザクションが終了すると、このカウンタを 1 減らします。 RDBMS がセーブポイントをサポートしている場合は、初回以外の beginNestedTransaction() メソッドのコール時に MDB2 が自動的にセーブポイントを設定し、その名前を返します。 このセーブポイントは、 completeNestedTransaction() メソッドによって自動的に開放されます。自動生成されるセーブポイントの名前は、 オプション "savepoint_format" (デフォルトは 'MDB2_SAVEPOINT_%s') とトランザクションカウンタの値によって決まります。
入れ子状のトランザクションを最初にオープンした後で MDB2 インスタンスが予期せぬ PEAR エラーを発生させた場合は、 トランザクションがロールバックされます。それ以外の場合はこの場所でコミットされます。 getNestedTransactionError() メソッドを使用すると、トランザクション内でエラーが発生したかどうかを調べることができます。 あるいは、 failNestedTransaction() で強制的にロールバックさせることも可能です。 このメソッドはオプションのパラメータを指定することができます。これは getNestedTransactionError() メソッドがコールされた際に返されるエラーとなります。 また二番目の boolean のパラメータとして強制的にロールバックさせるかどうかを指定します。
入れ子状のトランザクションのエミュレート
<?php
$mdb2->beginNestedTransaction(); # トランザクションをオープンします
$query = "INSERT INTO autoinc (id) VALUES (?)";
$stmt = $mdb2->prepare($query);
$stmt->execute(array(1));
$savepoint = $mdb2->beginNestedTransaction(); # 無視されます / セーブポイントを設定します
..
// この例では決して true にはなりません
if (false) {
// エラーを発生させます
$error = $mdb2->raiseError(MDB2_ERROR, null, null, 'kaboom');
$mdb2->failNestedTransaction();
}
if(($error = $mdb2->getNestedTransactionError())) {
die($error->getUserinfo());
}
$mdb2->completeNestedTransaction(); # 無視されます / セーブポイントを開放します
$stmt->execute(array(2));
$mdb2->completeNestedTransaction(); # コミットします
?>
最後に、MDB2 は SQL 92 標準にしたがったトランザクション分離レベルの設定を サポートしています。この設定には setTransactionIsolation() メソッドを使用します。 使用している RDBMS がその分離レベルをサポートしていないが、 より厳しい分離レベルをサポートしているという場合は、MDB2 は黙ってその厳しい分離レベルを使用します。 いくつかの RDBMS では追加のオプションをサポートしていますが、 MDB2 でサポートしていないものについては黙って無視されます。
トランザクション分離レベルの設定
<?php
$options = array('wait' => 'WAIT', 'rw' => 'READ WRITE');
$options = array('wait' => 'NO WAIT', 'rw' => 'READ ONLY');
$isolation_level = READ UNCOMMITTED # (ダーティリードを許可します)
$isolation_level = READ COMMITTED # (ダーティリードを防ぎます)
$isolation_level = REPEATABLE READ # (再読み込み不可能な読み込みを防ぎます)
$isolation_level = SERIALIZABLE # (ファントムリードを防ぎます)
$mdb2->setTransactionIsolation($isolation_level, $options);
?>