首页 > 编程笔记 > PHP笔记

PHP PDO事务处理

前面我们介绍了 MySQL 中的事务处理,其实在 PDO 中也是支持事务的,而且在 PDO 中使用事务很容易。前面我们已经介绍了构建和执行 SQL 语句的方式,当需要使用事务时,只要在执行 SQL 语句前使用 PDO::beginTransaction() 方法来启动事务即可。如果数据库底层驱动不支持事务,则抛出一个 PDOException 异常。事务一旦启动,可以使用 PDO::commit() 方法来提交事务,或者使用 PDO::rollBack() 方法来回滚事务。

PDO::beginTransaction() 方法的作用是启动一个事务;当事务中的所有 SQL 语句全部执行成功后,可以使用 PDO::commit() 方法来结束并提交一个事务;如果事务中某个 SQL 语句执行失败了,那么可以使用 PDO::rollBack() 方法来结束并回滚事务,事务中的所有 SQL 语句都将无效。注意,事务中的 SQL 语句要么全部成功执行,要么根本就不执行。

事务通常是通过把一批要执行的 SQL 语句“积蓄”起来,然后使之同时生效而,这样做的好处就是可以大大地提高这些 SQL 语句的执行的效率。换句话说,事务可以使脚本更快,而且更加健壮。

需要注意的是,并非每种数据库都支持事务,因此当第一次打开连接时,PDO 需要在“自动提交”模式下运行。自动提交模式意味着,如果数据库支持,运行的每个查询都有它自己的隐式事务,如果数据库不支持事务,则没有。

提示:PDO 仅在驱动层检查是否具有事务处理能力。如果某些运行时条件意味着事务不可用,且数据库服务接受请求去启动一个事务,PDO::beginTransaction() 将仍然返回 TRUE 而且没有错误。

当脚本结束或连接即将被关闭时,如果尚有未完成的事务,那么 PDO 将自动回滚这个事务。这种安全措施有可以避免在脚本意外终止时出现数据不一致的情况。也就是说,如果没有显式地提交事务,那么当某个地方出错时,将执行回滚来保证数据安全。

下面通过一个简单示例来演示以下 PDO 中事务的使用。假设有 userA 和 userB 两个银行账户,现在使用 userA 账户向 userB 账号中转账,使用事务来保证 userA 账户中转出一定金额的同时,userB 账户中转入相等的金额。

存放 userA 和 userB 两个账户的数据表的表结构如下所示:

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (
    `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
    `name` varchar(45) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '用户名',
    `cash` decimal(9, 2) NOT NULL COMMENT '账户余额',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

INSERT INTO `account` VALUES (1, 'userA', 100000.00);
INSERT INTO `account` VALUES (2, 'userB', 90000.00);

具体的实现代码如下所示:
<?php
    $dsn      = "mysql:dbname=testdb;host=localhost";
    $user     = "root";
    $password = "root";
    $dbh      = new PDO($dsn, $user, $password);
    //使用事务之前,要先关闭自动提交。不关闭的话,出现异常的时候没法回滚。
    //据手册描述,ATTR_AUTOCOMMIT 属性只在 mysql、OCI(oracle)、firebird 三种数据库中可用
    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
    $price = 996;
    try {
        $dbh->beginTransaction();
        //用户A账户扣除指定的金额
        $sqlcmd    = "UPDATE account SET cash=cash-$price WHERE name='userA'";
        $affected_rows = $dbh->exec($sqlcmd);
        if ($affected_rows > 0) {
            echo "用户A账户扣除成功" . "<br>";
        } else {
            throw new Exception("用户A账户扣除失败");
        }
        //用户B账户增加指定的金额
        $affected_rows = $dbh->exec("UPDATE account SET cash=cash+$price WHERE name='userB'");
        if ($affected_rows > 0) {
            echo "用户B账户增加成功" . "<br>";
        } else {
            throw new Exception("用户B账户增加失败");
        }
        echo "转账成功";
        //若前面两个步骤都成功,则提交事务
        $dbh->commit();
    }catch (PDOException $e) //若前面两个步骤中出现了异常,则回滚
    {
        echo $e->getMessage();
        $dbh->rollback();
    }
    //对事务的使用结束之后,重新开启自动提交
    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
?>

所有教程

优秀文章