忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

PHP PDO(PHP Data Objects)クラスを使ったトランザクションについて


PDO を使ったデータベースへの接続とテーブルレコード取得、及びデータレコードに対しての登録(INSERT)、更新(UPDATE)、削除(DELETE)について、以下の記事で説明しました。

PHP PDO(PHP Data Objects)クラスを使ったデータベースへのアクセスについて
PHP PDO(PHP Data Objects)クラスを使ったデータベースへのアクセス(登録、更新、削除)について

そこで、今回はデータ処理に関連して重要な方法であるトランザクション処理について説明します。

トランザクション処理 とは複数実行されるデータの登録(INSERT)、更新(UPDATE)、削除(DELETE)処理を一時的に蓄えておいて、 全ての処理が正常であれば、一括でデータベースに反映させるものです。
尚、どれかの処理でエラーが発生した場合には、全ての処理を取止め、データベースの状態をトランザクション開始の前の状態に戻せます。

トランザクション処理 を行うには以下の PDO クラスのメソッドを使います。

  • PDO::beginTransaction — トランザクションを開始する
  • PDO::commit — トランザクションをコミットする(データベースへの一括反映)
  • PDO::rollBack — トランザクションをロールバックする(トランザクション開始以降の処理を廃棄)

これらのメソッドは 返り値 として成功した場合に TRUE を、失敗した場合に FALSE を返します。

トランザクション処理 の実装としては以下の様に行います。(実際の処理では無く日本語で記述しています)

//	データベースへの接続
$pdo = new PDO( ... );

//	トランザクション開始
$ret = $pdo->beginTransaction();
if ($ret == true) {
	//	トランザクション開始が正常の場合

	//	複数のデータの登録(INSERT)、更新(UPDATE)、削除(DELETE)の処理
	...

	if (データ処理が正常チェック) {
		//	正常の場合
		$ret = $pdo->commit();    // コミット処理
	} else {
		//	エラーの場合
		$ret = $pdo->rollBack();  // ロールバック処理
	}
} else {
	//	トランザクション開始がエラーの場合
	//	(必要であれば処理記述)
}

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




■トランザクション・コミットの例

トランザクション・コミットの例として、最初のテーブルデータを全て削除(DELETE)後、2件のデータを追加(INSERT)します。 最後にコミットを行い、テーブルの全データの一覧を表示します。

<?php
ob_start(function($buf){ return mb_convert_encoding($buf, 'SJIS', 'UTF-8'); });
//  MySQLデータベースに接続
$dsn      = "mysql:host=localhost;dbname=pdo;";
$user     = 'root';
$password = 'password';
try {
    //  PDOクラス生成(データベース接続)
    $pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    //  エラー発生
    die('データベース接続ERROR:'.$e->getMessage()."\n");
}

//  === 全データの取得・表示 ===
function dispAll($_pdo) {
    $sql = "select * from `tm_shohin` order by id"; //  全データ取得SQL
    $pdostmt = $_pdo->prepare($sql);                //  SELECTクエリの準備
    $pdostmt->execute();                            //  SELECTクエリの実行
    while ($result = $pdostmt->fetch()) {
        echo "id:".$result["id"]." name :".$result["name"]." price:".$result["price"]."\n";
    }
    echo "\n";
}

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

//  全レコード削除(UPDATE)SQL
$sql = "delete from `tm_shohin`";
//  SQL文の準備
$pdostmt = $pdo->prepare($sql);
//  SQL文の実行
$ret = $pdostmt->execute();

//  レコード登録(INSERT)SQL
$sql = "insert into `tm_shohin` (id, name, price) VALUE (:id, :name, :price)";
//  SQL文の準備
$pdostmt = $pdo->prepare($sql);
//  SQL文の実行([id:1]のデータ)
$ret = $pdostmt->execute(array(":id" => 1, ":name" => 'パソコン001', ":price" => 100000));
//  SQL文の実行([id:2]のデータ)
$ret = $pdostmt->execute(array(":id" => 2, ":name" => 'パソコン002', ":price" => 202000));

//	コミット
$ret = $pdo->commit();

//  処理後の表示
dispAll($pdo);

//  接続を閉じる
$pdo = null;
?>

これを実行すると以下の様に表示されます。2件のレコードが追加されたのがわかります。

C:\xampp\htdocs\_test>php pdo12.php
id:1 name :パソコン001 price:100000
id:2 name :パソコン002 price:202000
 



■トランザクション・ロールバックの例

トランザクション・ロールバックの例として、2件のデータを追加(INSERT)し, 最後にロールバックを行い、テーブルの全データの一覧を表示します。

<?php
ob_start(function($buf){ return mb_convert_encoding($buf, 'SJIS', 'UTF-8'); });
//  MySQLデータベースに接続
$dsn      = "mysql:host=localhost;dbname=pdo;";
$user     = 'root';
$password = 'password';
try {
    //  PDOクラス生成(データベース接続)
    $pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    //  エラー発生
    die('データベース接続ERROR:'.$e->getMessage()."\n");
}

//  === 全データの取得・表示 ===
function dispAll($_pdo) {
    $sql = "select * from `tm_shohin` order by id"; //  全データ取得SQL
    $pdostmt = $_pdo->prepare($sql);                //  SELECTクエリの準備
    $pdostmt->execute();                            //  SELECTクエリの実行
    while ($result = $pdostmt->fetch()) {
        echo "id:".$result["id"]." name :".$result["name"]." price:".$result["price"]."\n";
    }
    echo "\n";
}

//  処理前の表示
dispAll($pdo);

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

//  レコード登録(INSERT)SQL
$sql = "insert into `tm_shohin` (id, name, price) VALUE (:id, :name, :price)";
//  SQL文の準備
$pdostmt = $pdo->prepare($sql);
//  SQL文の実行([id:3]のデータ)
$ret = $pdostmt->execute(array(":id" => 3, ":name" => 'パソコン003', ":price" => 303000));
//  SQL文の実行([id:4]のデータ)
$ret = $pdostmt->execute(array(":id" => 4, ":name" => 'プリンタ001', ":price" => 50000));

//	ロールバック
$ret = $pdo->rollback();

//  処理後の表示
dispAll($pdo);

//  接続を閉じる
$pdo = null;
?>

これを実行すると以下の様に表示されます。INSERT 処理されたデータが登録されていないことがわかります。

C:\xampp\htdocs\_test>php pdo13.php
id:1 name :パソコン001 price:100000
id:2 name :パソコン002 price:202000

id:1 name :パソコン001 price:100000
id:2 name :パソコン002 price:202000
 



■トランザクション・コミットせずにスクリプトが終了した場合

上記のスクリプトからロールバックの部分を忘れた状況にしてみます。

<?php
ob_start(function($buf){ return mb_convert_encoding($buf, 'SJIS', 'UTF-8'); });
//  MySQLデータベースに接続
$dsn      = "mysql:host=localhost;dbname=pdo;";
$user     = 'root';
$password = 'password';
try {
    //  PDOクラス生成(データベース接続)
    $pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    //  エラー発生
    die('データベース接続ERROR:'.$e->getMessage()."\n");
}

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

//  レコード登録(INSERT)SQL
$sql = "insert into `tm_shohin` (id, name, price) VALUE (:id, :name, :price)";
//  SQL文の準備
$pdostmt = $pdo->prepare($sql);
//  SQL文の実行([id:3]のデータ)
$ret = $pdostmt->execute(array(":id" => 3, ":name" => 'パソコン003', ":price" => 303000));
//  SQL文の実行([id:4]のデータ)
$ret = $pdostmt->execute(array(":id" => 4, ":name" => 'プリンタ001', ":price" => 50000));

//  接続を閉じる
//$pdo = null;
?>

これを実行し、MySQLから直接一覧を取ると以下の様に表示されます。INSERT 処理されたデータが登録されていないことがわかります。

MariaDB [pdo]> select * from tm_shohin;
+----+-------------+--------+
| id | name        | price  |
+----+-------------+--------+
|  1 | パソコン001 | 100000 |
|  2 | パソコン002 | 202000 |
+----+-------------+--------+
2 rows in set (0.00 sec)
 

これはトランザクションが開始され途中でコミットをせずにスクリプトが終わるとロールバックされるからです。
尚、接続を閉じるための PDO オブジェクトの null 設定を省いていますが、 PDO オブジェクトもスクリプトが終了する時には廃棄されるのと同時に、接続は閉じられます。


■トランザクション・一般的な例

トランザクション処理を行うデータ更新処理の一般的な例としては、以下の様な処理になるかと思います。

<?php
ob_start(function($buf){ return mb_convert_encoding($buf, 'SJIS', 'UTF-8'); });
//  MySQLデータベースに接続
$dsn      = "mysql:host=localhost;dbname=pdo;";
$user     = 'root';
$password = 'password';
try {
    //  PDOクラス生成(データベース接続)
    $pdo = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    //  エラー発生
    die('データベース接続ERROR:'.$e->getMessage()."\n");
}

//	トランザクション開始
$ret = $pdo->beginTransaction();
if ($ret == true) {
    //	トランザクション開始OK
    try {
        //  レコード登録(INSERT)SQL
        $sql = "insert into `tm_shohin` (id, name, price) VALUE (:id, :name, :price)";
        //  SQL文の準備
        $pdostmt = $pdo->prepare($sql);
        //  SQL文の実行([id:3]のデータ)
        if ($ret == true) {
            $ret = $pdostmt->execute(array(":id" => 3, ":name" => 'パソコン003', ":price" => 303000));
        }
        //  SQL文の実行([id:4]のデータ)
        if ($ret == true) {
            $ret = $pdostmt->execute(array(":id" => 4, ":name" => 'プリンタ001', ":price" => 50000));
        }

        //  その他処理があれば続く...

    } catch (Exception $e) {
        //  何かスクリプト上のエラーが在った!!
        $ret = false;
    }
    //  トランザクション終了処理
    if ($ret == true) {
        //  コミット
        $pdo->commit();
    } else {
        //  ロールバック
        $pdo->rollback();
    }
} else {
    //	トランザクション開始NG
}

//  接続を閉じる
$pdo = null;
?>













PR

コメント

コメントを書く