PHPプログラミング

ベストプラクティス

データベース PDO その10

PDOのトランザクション

  • このエントリーをはてなブックマークに追加

データベースの更新系の処理において欠かすことのできないトランザクション。トランザクションを実装する理由について解説した上で、PDOでの実用的なコード例を紹介します。

目次

  1. #  PDOでトランザクションを実装する
  2. #  データベースのトランザクションについて
  3. #  PDOのトランザクションの基本的な流れを確認する
  4. #  MySQLでトランザクションを実装する
  5. #  PostgreSQLでトランザクションを実装する
  6. #  SQLiteでトランザクションを実装する

PDOでトランザクションを実装する

今回はPDOでトランザクションを実装する方法について解説します。
次のような方が対象です。

  • データベースのトランザクションについて知りたい
  • PDOでトランザクションを実装したい
  • PDOを使ってMySQL、PostgreSQL、SQLiteのトランザクションを実装したい

PDO自体の基本的な内容については「PDOについて」を、データベースへの接続方法については「PDOを使ってデータベースへ接続」をご覧ください。

データベースのトランザクションについて

まずはデータベースの「トランザクション」についてご紹介します。
ここのセクションだけでトランザクションがどういうことを行うか、どうしてわざわざ使う必要があるかをザックリ理解することができます。

データベースにおけるトランザクションは、複数のデータ更新処理(参照を除く新規登録、更新、削除)を「コミット(処理実行)」の命令があるまで仮実行(下書きのようなイメージ)に留めておき、コミットの命令が出された時に一括して決定処理を行います。

トランザクションのイメージ

トランザクションのイメージ

処理を一度のタイミングで集中して行うため、複数の更新処理を行う場合は基本的に処理速度が向上します

また、トランザクション処理を行なっている間は、「コミット(処理実行)」か「ロールバック(処理取り消し)」のいずれかが行われるまでは他のトランザクション処理を受け付けません。
そのため、データベースの同時アクセスによるデータの行き違い(データの不整合)も未然に防ぐことができます。

以上の理由から、適切なタイミングでトランザクション処理を行うとシステムの高速化&堅牢性を同時に向上させる効果があります。

PDOのトランザクションの基本的な流れを確認する

PDOではトランザクションを簡単に実装する機能が備わっています。
ここで基本的な流れをここでご紹介し、続いてMySQL、PostgreSQL、SQLiteのそれぞれのデータベースごとに実装する例をご紹介していきます。

PDOのトランザクションコード例

try {
	// DBへ接続
	$dbh = new PDO("mysql:host=127.0.0.1; dbname=test; charset=utf8", 'username', 'password');

	// トランザクション開始
	$dbh->beginTransaction();

	// ここにデータベース更新系の処理が入る

	// コミット
	$dbh->commit();

} catch(PDOException $e) {

	// ロールバック
	$dbh->rollBack();

	// エラーメッセージ出力
	echo $e->getMessage();
	die();
}

// 接続を閉じる
$dbh = null;

beginTransactionメソッドがトランザクションの開始の合図となります。
この合図から、「commitメソッド」か「rollBackメソッド」が実行されるまで処理を仮状態とし、いずれかのメソッドが呼び出された瞬間に全反映 or 全キャンセルが実行されます。

トランザクションの途中でデータベースが何かしらのエラーを起こした場合はPDOExceptionオブジェクトの例外が投げられるので、上記のようにtry文〜catch文で囲む形が基本的な使い方となります。

全て正常であればcommitメソッドで処理を実行し、もし途中でエラーが起こってしまった場合はcatch文でPDOExceptionオブジェクトを受け取ってrollBackメソッドでデータベースを処理実行前の状態に巻き戻します。

以上のように、トランザクションを取り入れることで処理全体が正常に終了した場合のみデータベースを更新し、一部でもうまく行かなかった場合は処理前の状態に戻すことでデータの整合性を保つことができます。

MySQLでトランザクションを実装する

まずはMySQLからトランザクションを実装していきます。
操作前のテーブルには次のデータが入っています。

操作前のテーブル

操作前のテーブル(MySQL)

ここでは、id4のデータを更新していきます。
commitメソッドrollBackメソッドの違いについて、それぞれ実行することで解説していきます。

まずはcommitメソッドを使った通常の更新処理を実行してみます。

index.php

<?php

// 変数の初期化
$sql = null;
$res = null;
$dbh = null;

try {
	// DBへ接続
	$dbh = new PDO("mysql:host=127.0.0.1; dbname=test; charset=utf8", 'username', 'password');

	// トランザクション開始
	$dbh->beginTransaction();

	// UPDATE
	$sql = "UPDATE user_list SET age = 24 WHERE id = 4";

	// クエリ実行
	$res = $dbh->query($sql);

	// コミット
	$dbh->commit();

} catch(PDOException $e) {
	
	// ロールバック
	$dbh->rollBack();

	// エラーメッセージ出力
	echo $e->getMessage();
	die();
}

// 接続を閉じる
$dbh = null;

正常に処理されると、「テスト二郎」さんのage24に更新されます。

操作後のテーブル

操作後のテーブル(MySQL)

上記の処理ではcommitメソッドが実行されるまではデータの更新も保留状態となり、実行されたタイミングで初めてデータベースへ反映されます。
この挙動を確かめるために、次はあえてrollBackメソッドを実行してみます。

先ほど更新した「テスト二郎」さんのageを、次は25になるようにSQLを変更して処理を実行します。

index.php

<?php

// 変数の初期化
$sql = null;
$res = null;
$dbh = null;

try {
	// DBへ接続
	$dbh = new PDO("mysql:host=127.0.0.1; dbname=test; charset=utf8", 'username', 'password');

	// トランザクション開始
	$dbh->beginTransaction();

	// UPDATE
	$sql = "UPDATE user_list SET age = 25 WHERE id = 4";

	// クエリ実行
	$res = $dbh->query($sql);

	// 強制的にロールバックする
	$dbh->rollBack();

} catch(PDOException $e) {
	
	// ロールバック
	$dbh->rollBack();

	// エラーメッセージ出力
	echo $e->getMessage();
	die();
}

// 接続を閉じる
$dbh = null;

操作後のテーブル (ロールバック)

操作後のテーブル(MySQL)

上記のコードは処理が正常であっても強制的にロールバックを実行するため、何度実行してもデータを更新できないことが確認できます。

以上、MySQLでのトランザクションでした。

PostgreSQLでトランザクションを実装する

続いて、PostgreSQLでトランザクションを実装します。
基本的なコードは上記MySQLと同様です。

操作前のテーブル

操作前のテーブル(PostgreSQL)

今回はid5を持つ「テスト奈津子」さんのデータを更新します。

index.php

<?php

// 変数の初期化
$sql = null;
$res = null;
$dbh = null;

try {
	// DBへ接続
	$dbh = new PDO("pgsql:host=127.0.0.1; dbname=test;", 'username', 'password');

	// SQL作成
	$sql = "UPDATE user_list SET age = 30 WHERE id = 5";

	// SQL実行
	$res = $dbh->query($sql);

	// コミット
	$dbh->commit();

} catch(PDOException $e) {

	// ロールバック
	$dbh->rollBack();

	echo $e->getMessage();
	die();
}

// 接続を閉じる
$dbh = null;

操作後のテーブル

操作後のテーブル(PostgreSQL)

処理が正常に実行されると、テスト奈津子さんのage30に更新されます。
もし途中でエラーが起こった場合はcatch文へと移動し、rollBackメソッドによってデータベースが処理前の状態に復帰されます。

以上、PostgreSQLでのトランザクションでした。

SQLiteでトランザクションを実装する

最後にSQLiteでトランザクションを実装します。
全体の流れは上記2つのデータベースと同じ内容です。

操作前のテーブル

操作前のテーブル(SQLite)

ここでは、id4の「テスト有希」さんのデータを更新していきます。

index.php

<?php

// 変数の初期化
$sql = null;
$res = null;
$dbh = null;

try {
	// DBへ接続
	$dbh = new PDO("sqlite:./sqlite/test.sqlite3");

	// SQL作成
	$sql = "UPDATE user_list SET age = 28 WHERE id = 4";

	// SQL実行
	$res = $dbh->query($sql);

	// コミット
	$dbh->commit();

} catch(PDOException $e) {

	// ロールバック
	$dbh->rollBack();

	echo $e->getMessage();
	die();
}

// 接続を閉じる
$dbh = null;

操作後のテーブル

操作後のテーブル(SQLite)

処理が正常に実行されると、テスト有希さんのage28に更新されます。
もし途中でエラーが発生した場合はcatch文へ処理が移動し、rollBackメソッドによってデータベースが処理前の状態へ復帰します。

以上、SQLiteでのトランザクションでした。

  • このエントリーをはてなブックマークに追加