热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

通过PDO扩展与MySQL数据库交互实现增删改查实现和数据库事务

本文介绍通过PDO扩展与MySQL数据库交互实现增删改查实现和数据库事务。

相关学习推荐:mysql教程

通过预处理语句进行增删改查

为什么使用预处理语句

关于预处理语句我们在上篇教程中已经简单介绍过,我们可以将其与视图模板类比,所谓预处理语句就是预定义的 SQL 语句模板,其中的具体参数值通过占位符替代:

INSERT INTO REGISTRY (name, value) VALUES (?, ?)
INSERT INTO REGISTRY (name, value) VALUES (:name, :value)

然后在后续真正要执行 SQL 语句之前,再通过特定 API 方法将具体参数值与对应占位符进行绑定和映射。就好比定义的视图模板也是将变量通过特定占位符替代,然后真正渲染时将变量值传递进来填充和渲染一样。

为什么要费这番周折呢?直接用前面演示的 query 方法进行增删改查操作它不香吗?呃,那我们接下来来说说预处理语句的好处,或者说为什么要使用预处理语句进行数据库交互,好处有二:

  • 首先,使用预处理语句提前定义的 SQL 模板只会解析一次,但可以通过传递不同的参数值执行多次,从而避免模板相同的 SQL 语句重复分析、编译和优化,提高数据库操作执行速度;
  • 其次,后期传递给预处理语句的参数值会被底层驱动进行处理,从而有效避免 SQL 注入攻击。

综上,从性能和安全角度考虑,推荐使用预处理语句处理数据库的增删改查操作。

增删改查示例代码

接下来,我们基于 PDO 提供的预处理语句 API 实现 MySQL 数据库的增删改查操作,我们将通过面向对象的方式来实现:

pdo = $pdo;
        }
    }

    public function insert($title, $content)
    {
        $sql = 'INSERT INTO `post` (title, content, created_at) VALUES (:title, :content, :created_at)';
        try {
            // 准备预处理语句
            $stmt = $this->pdo->prepare($sql);
            // 获取当前时间对应的格式化字符串:2020-05-28 13:00:00
            $datetime = date('Y-m-d H:i:s', time());
            // 绑定参数值
            $stmt->bindParam(':title', $title, PDO::PARAM_STR);
            $stmt->bindParam(':content', $content, PDO::PARAM_STR);
            $stmt->bindParam(':created_at', $datetime, PDO::PARAM_STR);
            // 执行语句
            $stmt->execute();
            return $this->pdo->lastInsertId();  // 返回插入记录对应ID
        } catch (PDOException $e) {
            printf("数据库插入失败: %s\n", $e->getMessage());
        }
    }

    public function select($id)
    {
        $sql = 'SELECT * FROM `post` WHERE id = ?';
        try {
            // 准备预处理语句
            $stmt = $this->pdo->prepare($sql);
            // 绑定参数值
            $stmt->bindValue(1, $id, PDO::PARAM_INT);
            // 执行语句
            $stmt->execute();
            return $stmt->fetchObject(self::class);  // 以对象方式返回结果集
        } catch (PDOException $e) {
            printf("数据库查询失败: %s\n", $e->getMessage());
        }
    }

    public function selectAll()
    {
        $sql = 'SELECT * FROM `post` ORDER BY id DESC';
        try {
            // 准备预处理语句
            $stmt = $this->pdo->prepare($sql);
            // 执行语句
            $stmt->execute();
            return $stmt->fetchAll();  // 返回所有结果集
        } catch (PDOException $e) {
            printf("数据库查询失败: %s\n", $e->getMessage());
        }
    }

    public function update($id)
    {
        $sql = 'UPDATE `post` SET created_at = :created_at WHERE id = :id';
        try {
            // 准备预处理语句
            $stmt = $this->pdo->prepare($sql);
            $datetime = date('Y-m-d H:i:s', time());
            // 绑定参数值
            $stmt->bindParam(':created_at', $datetime, PDO::PARAM_STR);
            $stmt->bindValue(':id', $id, PDO::PARAM_INT);
            // 执行语句
            $stmt->execute();
            return $stmt->rowCount();
        } catch (PDOException $e) {
            printf("数据库更新失败: %s\n", $e->getMessage());
        }
    }

    public function delete($id)
    {
        $sql = 'DELETE FROM `post` WHERE id = ?';
        try {
            // 准备预处理语句
            $stmt = $this->pdo->prepare($sql);
            // 绑定参数值
            $stmt->bindValue(1, $id, PDO::PARAM_INT);
            // 执行语句
            $stmt->execute();
            return $stmt->rowCount();
        } catch (PDOException $e) {
            printf("数据库删除失败: %s\n", $e->getMessage());
        }
    }
}

我们构建了一个 Post 类,然后在构造函数中初始化 $pdo 实例(从外部传入),然后将基于预处理语句实现的增删改查操作分解到对应的类方法中。整体逻辑非常简单,以 insert 为例,首先通过 PDO 对象的 prepare 方法传入 SQL 模板构建预处理语句,该方法返回 PDOStatement 对象,接下来,就是调用该对像的 bindParam 方法绑定具体参数值,该方法的第一个参数是占位符,第二个参数是参数值,第三个参数是值类型(对应的常量可以在 PDO 预定义常量中查询),绑定好参数后,就可以调用 PDOStatement 对象的 execute 方法执行预处理语句了。

对于插入操作,可以通过 PDO 对象上的 lastInsertId 方法返回插入记录的主键 ID,对于更新和删除方法,可以通过 PDOStatement 对象上的 rowCount 方法返回受影响行数表示是否操作成功。对于查询操作,可以通过 PDOStatement 对象的 fetch 方法返回单条记录,也可以通过 fetchObject 方法返回映射到指定类后的对象实例(也是单条记录),对于多个结果,可以通过 fetchAll 方法返回。

需要注意的是,在声明预处理语句的时候,可以通过 ? 占位符,也可以通过 :name 这种可读性更好的占位符,然后在绑定参数时,既可以通过 bindValue 也可以通过 bindParam 方法,两者传递参数一样,只是对于 ? 占位符,需要通过数值序号建立与 SQL 模板的映射(从 1 开始)。

结合代码和 PHP 官方文档理解上面的代码并不困难,接下来,我们来编写测试代码:

// 初始化 PDO 连接实例
$dsn = 'mysql:host=127.0.0.1;port=3306;dbname=test;charset=utf8mb4';
$user = 'root';
$pass = 'root';
try {
    $pdo = new PDO($dsn, $user, $pass);
} catch (PDOException $e) {
    printf("数据库连接失败: %s\n", $e->getMessage());
}

// 测试代码
$post = new Post($pdo);
// insert
$title = '这是一篇测试文章';
$cOntent= '测试内容: 今天天气不错';
$id = $post->insert($title, $content);
echo '文章插入成功: ' . $id . '
'; // select $item = $post->select($id); echo '
';
print_r($item);
// update
$affected = $post->update($id);
echo '受影响的行数: ' . $affected . '
'; // delete $affected = $post->delete($id); echo '受影响的行数: ' . $affected . '
'; // selectAll $items = $post->selectAll(); print_r($items);

初始化一个 PDO 对象实例传入 Post 构造函数,然后依次调用 Post 对象的增删改查方法。在浏览器中访问,打印结果如下:

更多模式设置,请参考官方文档中 fetchAll 方法的介绍和示例。

数据库事务

最后,我们再来看看如何通过 PDO 扩展实现数据库事务的提交和回滚,我们已经知道,对于单条 SQL 语句而言,事务提交和回滚是自动完成的,对于 SQL 语句序列(多条 SQL 语句),则需要显式开启事务和提交事务,PDO 对象也为此提供了对应的 API 方法。非常简单,比如我们在 Post 类中新增一个批量插入方法 batchInsert 方法:

public function batchInsert(array $items)
{
    $sql = 'INSERT INTO `post` (title, content, created_at) VALUES (:title, :content, :created_at)';
    try {
        // 开启事务
        $this->pdo->beginTransaction();
        // 准备预处理语句
        $stmt = $this->pdo->prepare($sql);
        foreach ($items as $item) {
            // 绑定参数值
            $datetime = date('Y-m-d H:i:s', time());
            $stmt->bindParam(':title', $item->title, PDO::PARAM_STR);
            $stmt->bindParam(':content', $item->content, PDO::PARAM_STR);
            $stmt->bindParam(':created_at', $datetime, PDO::PARAM_STR);
            // 执行语句
            $stmt->execute();
        }
        $this->pdo->commit(); // 提交事务
        return $stmt->rowCount();  // 返回受影响的行数
    } catch (PDOException $e) {
        $this->pdo->rollBack(); // 回滚事务
        printf("数据库批量插入失败: %s\n", $e->getMessage());
    }
}

我们只需要在执行 SQL 序列之前调用 PDO 对象的 beginTransaction 方法开启事务,然后在所有 SQL 语句执行完成后调用 commit 方法提交事务,如果 SQL 执行过程中出错,则在异常处理代码中通过 PDO 对象的 rollBack 方法回滚事务。

为上述方法编写测试代码:

$post = new Post($pdo);
$items = [
    [
        'title' => '这是一篇测试文章111',
        'content' => '测试内容'
    ],
    [
        'title' => '这是一篇测试文章222',
        'content' => '测试内容'
    ],
    [
        'title' => '这是一篇测试文章333',
        'content' => '测试内容'
    ],
];
$post->batchInsert($items);
$items = $post->selectAll();
print_r($items);

执行这段代码,打印结果中包含新插入的文章数据,则表明事务提交成功:

以上就是通过PDO扩展与MySQL数据库交互 实现增删改查实现和数据库事务的详细内容,更多请关注 第一PHP社区 其它相关文章!


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 众筹商城与传统商城的区别及php众筹网站的程序源码
    本文介绍了众筹商城与传统商城的区别,包括所售产品和玩法不同以及运营方式不同。同时还提到了php众筹网站的程序源码和方维众筹的安装和环境问题。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • 本文介绍了一个免费的asp.net控件,该控件具备数据显示、录入、更新、删除等功能。它比datagrid更易用、更实用,同时具备多种功能,例如属性设置、数据排序、字段类型格式化显示、密码字段支持、图像字段上传和生成缩略图等。此外,它还提供了数据验证、日期选择器、数字选择器等功能,以及防止注入攻击、非本页提交和自动分页技术等安全性和性能优化功能。最后,该控件还支持字段值合计和数据导出功能。总之,该控件功能强大且免费,适用于asp.net开发。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
author-avatar
手机用户2502916257
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有