PHP MySQL Transaction

摘要:在本教程中,您将学习如何在 PHP 中处理 MySQL 事务以确保数据库数据的完整性。

PHP MySQL Transaction

事务是一组相互依赖的 SQL 语句,需要以“全有或全无”模式执行。如果所有 SQL 语句都成功执行,则事务成功。任何一条语句失败都会触发系统回滚到原始状态,避免数据不一致。

交易的一个典型示例是从一个银行账户到另一个银行账户的转账交易。它需要三个步骤:

  • 检查转账账户余额,查看转账金额是否足够。
  • 如果金额足够,则从转账账户余额中扣除该金额。
  • 将转账金额添加到收款账户的余额中。

如果第二步出现错误,则不应继续第三步。另外,如果第三步出现错误,则必须逆向执行第二步。如果交易失败,两个银行账户的金额均保持不变;如果交易成功完成,则两个银行账户的金额均会正确调整。

PHP 中的 MySQL 事务

当您使用 PDO创建与支持事务的数据库的连接时,将设置自动提交模式。这意味着您发出的每个查询都包含在隐式事务中。

请注意,并非所有 MySQL 中的存储引擎都支持事务,例如 MyISAM 不支持事务,但 InnoDB 却支持。

要在 PHP 中处理 MySQL 事务,请使用以下步骤:

  1. 通过调用 PDO 对象的beginTransaction()方法来启动事务。
  2. 将 SQL 语句和commit()方法调用放在try块中。
  3. 通过调用 PDO 对象的rollBack()方法来回滚catch块中的事务。

PHP MySQL 事务示例

我们将创建一个名为accounts来演示两个银行帐户之间的转账。

首先,执行以下语句创建accounts表:

CREATE TABLE accounts (
    id     INT AUTO_INCREMENT PRIMARY KEY,
    name   VARCHAR (50)    NOT NULL,
    amount DECIMAL (19, 4) NOT NULL
);Code language: SQL (Structured Query Language) (sql)

其次,在accounts表中插入两行:

INSERT INTO accounts(name,amount)
VALUES('John',25000),
      ('Mary',95000);Code language: SQL (Structured Query Language) (sql)

三、查询accounts表:

SELECT *
  FROM accounts;Code language: PHP (php)
PHP MySQL Transaction example

我们看一下下面的TransactionDemo类:

<?php

/**
 * PHP MySQL Transaction Demo
 */
class TransactionDemo {

    const DB_HOST = 'localhost';
    const DB_NAME = 'classicmodels';
    const DB_USER = 'root';
    const DB_PASSWORD = '';

    /**
     * Open the database connection
     */
    public function __construct() {
        // open database connection
        $conStr = sprintf("mysql:host=%s;dbname=%s", self::DB_HOST, self::DB_NAME);
        try {
            $this->pdo = new PDO($conStr, self::DB_USER, self::DB_PASSWORD);
        } catch (PDOException $e) {
            die($e->getMessage());
        }
    }

    /**
     * PDO instance
     * @var PDO 
     */
    private $pdo = null;

    /**
     * Transfer money between two accounts
     * @param int $from
     * @param int $to
     * @param float $amount
     * @return true on success or false on failure.
     */
    public function transfer($from, $to, $amount) {

        try {
            $this->pdo->beginTransaction();

            // get available amount of the transferer account
            $sql = 'SELECT amount FROM accounts WHERE id=:from';
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute(array(":from" => $from));
            $availableAmount = (int) $stmt->fetchColumn();
            $stmt->closeCursor();

            if ($availableAmount < $amount) {
                echo 'Insufficient amount to transfer';
                return false;
            }
            // deduct from the transferred account
            $sql_update_from = 'UPDATE accounts
				SET amount = amount - :amount
				WHERE id = :from';
            $stmt = $this->pdo->prepare($sql_update_from);
            $stmt->execute(array(":from" => $from, ":amount" => $amount));
            $stmt->closeCursor();

            // add to the receiving account
            $sql_update_to = 'UPDATE accounts
                                SET amount = amount + :amount
                                WHERE id = :to';
            $stmt = $this->pdo->prepare($sql_update_to);
            $stmt->execute(array(":to" => $to, ":amount" => $amount));

            // commit the transaction
            $this->pdo->commit();

            echo 'The amount has been transferred successfully';

            return true;
        } catch (PDOException $e) {
            $this->pdo->rollBack();
            die($e->getMessage());
        }
    }

    /**
     * close the database connection
     */
    public function __destruct() {
        // close the database connection
        $this->pdo = null;
    }

}

// test the transfer method
$obj = new TransactionDemo();

// transfer 30K from from account 1 to 2
$obj->transfer(1, 2, 30000);


// transfer 5K from from account 1 to 2
$obj->transfer(1, 2, 5000);Code language: PHP (php)

我们在__construct()方法中打开一个数据库连接,并在__destruct()方法中关闭它。transfer()方法中:

  • 首先,我们查询转账账户的amount ,并与转账金额进行比较,检查转账账户的余额是否充足。
  • 其次,如果金额足够,我们会从转账账户中扣除转账金额,添加到收款账户中。
  • 第三,我们通过调用commit()方法提交事务。如果发生任何错误,我们会调用catch块中的rollBack()方法来回滚事务。

让我们测试一下transfer()方法。

// transfer 30K from from account 1 to 2
$obj->transfer(1, 2, 30000);Code language: PHP (php)

我们将 30K 从 John 的帐户转至 Mary 的帐户。我们收到以下消息:

Insufficient amount to transfer

让我们进行另一次传输:

// transfer 5K from from account 1 to 2
$obj->transfer(1, 2, 5000);Code language: PHP (php)

该脚本返回以下消息:

The amount has been transferred successfully.

我们已成功在两个银行帐户之间转账。

您可以通过以下链接下载源代码:

下载 PHP MySQL 事务源代码

在本教程中,我们逐步向您展示了如何在 PHP 中处理 MySQL 事务以确保数据完整性。

本教程有帮助吗?