事务处理支持很长时间以来一直是大多数MySQL开发者的心愿,随着MySQL 4.0的发布,这个心愿最后终于得以实现。MySQL 4.0后不久,拥有一个新的MySQL插件的PHP 5.x也发布了。这个新插件,MySQL Improved,使得PHP开发者通过利用本地的PHP函数,获得了这些新的事务处理能力。这篇简短的教程将向你说明怎样利用这些新的MySQLi函数,用PHP实现以MySQL为基础的事务。 概要 如果你还不知道,那么我可以告诉你,事务只是一组SQL语句,通常因为它们是彼此相互依赖的,所以要在全有或全无(all-or-nothing)的模式下执行。只有当所有组成的语句都执行成功了,一个事务才算是成功了;任何一个语句中的失败应该都会导致系统“回滚”到它先前的状态,以避免数据连接/崩溃问题。 对于这一点,两个银行帐户间的转帐是一个很好的例子。在数据库级,这样的转帐包括两个步骤:首先,从源帐户中扣除转帐的金额,然后将其加到目标帐户中。如果在第二步中发生了错误,那么第一步就必须被取消,以避免不相符的情况(和愤怒的客户聚众滋事)。事务安全系统将自动地撤到系统先前的“快照”。 大多数数据库(包括MySQL)通过一个命令的组合来完成这个: * START TRANSACTION命令标志着一个新的事务组的开始。它后面常接一系列的SQL命令。 * COMMIT命令标志着一个事务组的结束,表示事务期间做的所有改变应该被提交或者使之永久化。 * ROLLBACK命令标志着一个事务组的结束,表示事务期间所做的所有改变应该被撤消。 PHP中的事务处理函数 PHP中的MySQLi插件引进了新的函数,帮助开发者利用MySQL的事务处理能力。实质上,这些函数对等地被叫做SQL START TRANSACTION,COMMIT和 ROLLBACK命令。列表A为你展示了一个例子,列表A: // connect to database $dbh = mysqli_connect($host, $user, $pass, $db); // turn off auto-commit mysqli_autocommit($dbh, FALSE); // run query 1 $result = mysqli_query($dbh, $query1); if ($result !== TRUE) { mysqli_rollback($dbh); // if error, roll back transaction } // run query 2 $result = mysqli_query($dbh, $query2); if ($result !== TRUE) { mysqli_rollback($dbh); // if error, roll back transaction } // and so on... // assuming no errors, commit transaction mysqli_commit($dbh); // close connection mysqli_close($dbh); ?> 在PHP 中执行一项事务有三个基本的步骤: * 第一步是始终关掉数据库的“auto-commit”,它实质上意味着系统在你作出改变时就保存它们。这一点是很重要的,因为在一个事务处理环境中,你应该只有在确定了所有事务处理的“unit”都成功完成了以后,才保存你所做的改变。你可以通过mysqli_autocommit()函数关掉数据库的自动提交。 * 接下来,通过mysqli_query()函数,继续用通常的方法进行INSERT、UPDATE和/或DELETE查询。检验每一个查询返回的值,弄清楚它是否成功了是很重要的。如果其中任何一个查询失败了,mysqli_rollback()函数就会被用来将系统返回到事务进行之前的状态。 * 假设组成事务组的所有命令都成功执行了,就要用mysqli_commit()函数将变化保存到数据库系统。请注意,一旦这个函数被调用,事务就不能被撤消了。 工作实例 要了解这个在实践中是怎么工作的,让我们回到前面讨论过的银行转帐的例子.我们假设你的任务是建立一个简单的Web应用程序,让用户在他们的银行帐户间转帐。我们再进一步假设一个单独用户的帐户存储在一个MySQL数据库中,如下所示: +----+------------+---------+ | id | label | balance | +----+------------+---------+ | 1 | Savings #1 | 1000 | | 2 | Current #1 | 2000 | | 3 | Current #2 | 3000 | +----+------------+---------+ 3 rows in set (0.34 sec) ここで、ユーザーが現金金額を入力し、ある口座から別の口座に送金できるシンプルなインターフェイスを構築する必要があります。実際の「トランザクション」は 2 つの UPDATE ステートメントで実行されます。1 つはソース アカウントから送金金額を引き出す (つまり、借方)、もう 1 つは送金金額を宛先アカウントに入金する (つまり、クレジット) ステートメントです。私たちが行っていることがアカウント間での送金であると仮定すると、すべてのアカウントの利用可能な合計残高 ($6000) は常に同じままであるはずです。 リスト B は考えられるコードを示しています。リスト B: // データベースに接続します $dbh = mysqli_connect("localhost", "user", "pass", "test") // 自動コミットをオフにする mysqli_autocommit($dbh, FALSE); // 乗り換えを探す if ($_POST[submit] && is_numeric($_POST[amt])) { // $$ をターゲットアカウントに追加します $result = mysqli_query($dbh, "UPDATE accounts SET if ($result !== TRUE) { mysqli_rollback($dbh); // エラーの場合はトランザクションをロールバックします } // ソースアカウントから $$ を減算します $result = mysqli_query($dbh, "アカウントを更新します if ($result !== TRUE) { mysqli_rollback($dbh); // エラーの場合、トランザクションをロールバックします } // エラーがないと仮定して、トランザクションをコミットします mysqli_commit($dbh); } // アカウント残高を取得します // 配列に保存し、フォームの生成に使用します $result = mysqli_query($dbh, "SELECT * FROM accounts"); while ($row = mysqli_fetch_assoc($result)) { $accounts[] = $row; } // 接続を閉じる mysqli_close($dbh); ?> フォームが完了して送信されると、2 つの UPDATE クエリが実際に借方操作と貸方操作の実行を開始します。各クエリの最後に mysqli_rollback() があり、クエリが失敗した場合にアクティブ化されることに注意してください。クエリが失敗しないと仮定すると、mysqli_commit() を呼び出すことによって、新しい収入と支出のテーブルがデータベースに保存されます。その時点でデータベース接続は閉じられます。 自分で試して、Saving #1 から Current #2 に $500 を送金することができます。移管を実行すると、図表 B に示す新しい貸借対照表の結果が表示されます。 ヒント: もちろん、これは単純な 2 つのコマンドのトランザクションです。通常、このトランザクション モデルは、一緒に実行される SQL ステートメントが多数あり、1 つのステートメントの失敗が他のステートメントに連鎖的に影響する場合に使用できます。このような場合、mysqli_query() と mysqli_rollback() の呼び出しを、必要なときに呼び出せる単一のユーザー定義関数に凝縮する方が簡単な場合があります。 ご覧のとおり、PHP と MySQL を使用してトランザクション処理モデルを実装すると、クエリ実行エラーに対して MySQL データベースをより堅牢にすることができます。ただし、コードを書き直してこのモデルを使用する前に、トランザクションによってシステム パフォーマンス管理のコストが増加することに注意してください。そのため、このモデルを実装する前に、費用対効果の分析を行うことを常にお勧めします。
mysql> SELECT * FROM accounts;
or die("Cannot connect");
Balance = Balance + " . $_POST[amt] . " WHERE id = " . $_POST[to]);
SET Balance = Balance - " . $_POST[amt] .
" WHERE id = " . $_POST[from]);
ご覧のとおり、スクリプトはデータベースに接続し、自動コミットをオフにすることで開始されます。次に、SELECT クエリを実行してすべての口座の現金の入出金を取得し、ドロップダウン インターフェイスを備えたテーブルを作成して、トランザクションに使用するソース/ターゲット口座を選択します。証拠品 A は元の形式を示しています。
初期フォーム
取引完了後のステータスです。