PHPプログラミング

レシピ

データベース MySQL / MariaDB

mysqliのトランザクション

データベースにおいて欠かすことのできない「トランザクション」。その役割を解説したうえで、実際にmysqliクラスを使ったコードとして書いていきます。

この記事のポイント

  • トランザクションを実装する理由が分かる
  • mysqliでのトランザクション実装方法が分かる

目次

データの整合性を保つためのトランザクション

MySQLなどの「データベース」は、多くのユーザーから膨大な数のアクセスがあります。
そのため、いつどこでエラーが起きるかも分かりません。
そんな過酷な環境でもデータを保持する必要があるため、整合性(矛盾のない理屈の通った状態)を保つ仕組みを備えています。
それが、「トランザクション」です。

イメージしやすいよう、極端な例を使ってもう少し具体的に説明します。
仮に、1つのデータベースに次のような2つのテーブルがあったとします。

2つのテーブル

「商品在庫」テーブルには取り扱っている商品の情報が、「売上管理」テーブルにはお客様からの注文履歴が入っています。

ある日、Aさんが通勤中にスマホでオンラインショップ利用しました。
お気に入りの商品を見つけ、「注文確定」ボタンを押した瞬間、なんと運悪く電車が地下に入ってしまい電波が切れてしまいました

アンテナが圏外に。

通信が途中で切れてしまったため、「商品在庫」のデータベースには注文についての中途半端なデータが渡されました
そのため、「商品在庫」のデータは在庫が1つ減りましたが、「売上管理」のデータはエラーで登録ができませんでした。

データベースへのデータ登録例

その結果、在庫は1つ減ったにも関わらず、売り上げが増えていないというおかしな現象が起こってしまいました。

このような、エラーによるデータの矛盾を防ぐ仕組みが「トランザクション」です。

トランザクションの図解

トランザクションを組み込むと、処理全体が成功したときのみデータを更新するようになります。
これを「コミット」と呼びます。

一方で、もし途中で1つでもエラーが出た場合は、処理全体が失敗したと判断して処理前の状態に戻ります。
2つのテーブルもリセットされます。
こちらは「ロールバック」と呼びます。

このように、処理結果をコミットしたか、ロールバックをしたかの2択となることで、片方のテーブルのみが更新されてしまうような矛盾を防ぐことができます。

トランザクションの役割が分かったところで、実際にコードで書いていきましょう。

オブジェクト型の書き方

まずはオブジェクト型でのコードを書いていきます。

$mysqli->set_charset(‘utf8’);」までのデータベースへ接続する部分については、「mysqliを使ってデータベースへ接続」を参照ください。
また、UPDATE文についての詳細は「mysqliでデータを更新(UPDATE)」を参照してください。

コード

$mysqli = new mysqli( 'host_name', 'user_name', 'password', 'database_name');

if( $mysqli->connect_errno ) {
	echo $mysqli->connect_errno . ' : ' . $mysqli->connect_error;
}

$mysqli->set_charset('utf8');

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

try {
	// UPDATE
	$sql = "UPDATE gc_granola SET price=400 WHERE id = 11";
	$res = $mysqli->query($sql);

} catch( Exception $e ){
	// エラーが起きたらロールバック
	$mysqli->rollback();
}

// 正常に終了したらコミット
$mysqli->commit();

$mysqli->close();

解説

トランザクションの流れは次のようになります。

  1. 1 トランザクション開始(begin_transaction
  2. 2 処理の実行
  3. 3 コミット or ロールバックでトランザクション解除

この流れを踏まえると、まず「$mysqli->begin_transaction();」でトランザクションを開始しています。

次に、SQLのUPDATE文でデータの更新を行います。ここの処理がtry〜catch文で囲まれていますが、これはデータベースの処理中に例外エラーが発生しないかを確認するためのものです。
もしエラーが発生した場合は、「$mysqli->rollback();」でロールバックが実行されます。

最後に、「$mysqli->commit();」の部分でコミットを行い、処理が終了します。
もしコミットする前にロールバックが実行されていたら、ここのコミットでは何もしません。

手続き型の書き方

次に、手続き型でのコードを書いていきます。

コード

$db_link = mysqli_connect( 'host_name', 'user_name', 'password', 'database_name');

if( mysqli_connect_errno($db_link) ) {
	echo mysqli_connect_errno($db_link) . ' : ' . mysqli_connect_error($db_link);
}

mysqli_set_charset( $db_link, 'utf8');

// トランザクション開始
mysqli_begin_transaction($db_link);
		
try {
	// UPDATE
	$sql = "UPDATE gc_granola SET price=400 WHERE id = 11";
	$res = mysqli_query( $db_link, $sql);
	throw new Exception('error'); 

} catch(Exception $e) {
	// エラーが起きたらロールバック
	mysqli_rollback($db_link);
}

// 正常に終了したらコミット
mysqli_commit($db_link);

解説

コードの大まかな流れはオブジェクト型と同様です。

しかし、今回はtry文であえて「throw new Exception(‘error’);」と強制的に例外エラーを発生させています。
この1行がある限り、catch文の中に記述した「mysqli_rollback($db_link);」が呼び出されるためデータが更新されることはありません。
この関数のおかげで、例外が起きたときにしっかりとcatch文の中のロールバックが実行されているかを確認することができます。

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

ありがとうございます。
コメントを送信しました。