관련 학습 권장 사항: mysql 튜토리얼
이전 튜토리얼에서 준비된 문을 간략하게 소개했는데, 이를 다음과 비교할 수 있습니다. 보기 템플릿, 소위 준비된 문은 특정 매개 변수 값이 자리 표시자로 대체되는 사전 정의된 SQL 문 템플릿입니다.
INSERT INTO REGISTRY (name, value) VALUES (?, ?) INSERT INTO REGISTRY (name, value) VALUES (:name, :value)
그런 다음 후속 SQL 문이 실제로 실행되기 전에 특정 매개 변수 값은 다음과 같습니다. 특정 API 메서드를 통해 전달된 매개변수 값은 해당 자리표시자에 바인딩되고 매핑됩니다. 정의된 뷰 템플릿과 마찬가지로 변수를 특정 자리 표시자로 대체한 다음 변수 값을 전달하여 실제 렌더링 중에 채우고 렌더링합니다.
이 모든 문제를 해결해야 하는 이유는 무엇입니까? 앞서 설명한 query
메소드를 직접 사용하여 추가, 삭제, 수정, 쿼리 작업을 수행하면 좋지 않을까요? 자, 준비된 문의 장점이나 데이터베이스 상호 작용을 위해 준비된 문을 사용해야 하는 이유에 대해 이야기해 보겠습니다. 두 가지 이점이 있습니다. query
方法进行增删改查操作它不香吗?呃,那我们接下来来说说预处理语句的好处,或者说为什么要使用预处理语句进行数据库交互,好处有二:
综上,从性能和安全角度考虑,推荐使用预处理语句处理数据库的增删改查操作。
接下来,我们基于 PDO 提供的预处理语句 API 实现 MySQL 数据库的增删改查操作,我们将通过面向对象的方式来实现:
<?php class Post { public $id; public $title; public $content; public $created_at; /** * @var PDO */ protected $pdo; public function __construct(PDO $pdo = null) { if ($pdo != null) { $this->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 . '<br>'; // select $item = $post->select($id); echo '<pre class="brush:php;toolbar:false">'; print_r($item); // update $affected = $post->update($id); echo '受影响的行数: ' . $affected . '<br>'; // delete $affected = $post->delete($id); echo '受影响的行数: ' . $affected . '<br>'; // selectAll $items = $post->selectAll(); print_r($items);
初始化一个 PDO 对象实例传入 Post
构造函数,然后依次调用 Post 对象的增删改查方法。在浏览器中访问,打印结果如下:
我们可以看到 fetchAll
方法默认返回的结果集数组中既包含索引映射,又包含字段名映射,这可以通过设置获取模式来解决,比如要返回 Post
对象数组,可以这么做:
return $stmt->fetchAll(PDO::FETCH_CLASS, self::class);
这样,返回的结果就是这样的了:
更多模式设置,请参考官方文档中 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()); } }
Post
클래스를 구축한 후 생성자에서 $pdo
인스턴스를 초기화하고(외부에서 전달) 추가, 삭제, 준비된 명령문을 기반으로 수정 및 쿼리 작업을 해당 클래스 메서드로 수행합니다. 전체적인 논리는 매우 간단합니다. 먼저, SQL 템플릿이 PDO 객체의 prepare 메소드를 통해 전달되어 PDOStatement 객체를 반환합니다. , 개체의 바인딩 매개변수 값이 호출됩니다. 이 메서드의 첫 번째 매개변수는 자리 표시자이고, 두 번째 매개변수는 매개변수 값이며, 세 번째 매개변수는 값 유형입니다(해당 상수는 PDO에서 쿼리할 수 있습니다). 미리 정의된 상수)를 잘 바인딩하세요. 매개 변수를 전달한 후 PDOStatement 개체의 실행 메서드를 호출하여 준비된 문을 실행할 수 있습니다. 🎜🎜삽입 작업의 경우 PDO 개체의 lastInsertId 메서드를 사용하여 삽입된 레코드의 기본 키 ID를 반환할 수 있습니다. 업데이트 및 삭제 메서드의 경우 PDOStatement 개체의 rowCount 메서드를 사용하여 영향을 받는 행 수를 반환할 수 있습니다. 작업이 성공했는지 여부를 나타냅니다. 쿼리 작업의 경우 PDOStatement 개체의 fetch 메서드를 통해 단일 레코드를 반환하거나 fetchObject 메서드를 사용하여 지정된 클래스(또한 단일 레코드)에 매핑된 개체 인스턴스를 반환할 수 있습니다. fetchAll 메소드를 통해 가져옵니다. 🎜🎜Prepared 문을 선언할 때 ?
자리 표시자를 사용하거나 더 읽기 쉬운 자리 표시자인 :name
을 사용할 수 있다는 점에 유의해야 합니다. 바인딩 매개변수의 경우, binValue 또는 binParam 메소드를 사용할 수 있습니다. 두 가지 모두에 전달된 매개변수는 동일하지만 ?
자리 표시자의 경우 숫자 순서를 통해 SQL 템플릿과의 매핑을 설정해야 합니다. 번호입니다(1부터 시작). 🎜🎜코드와 PHP 공식 문서를 결합하면 위 코드를 이해하는 것은 어렵지 않습니다. 다음으로 테스트 코드를 작성해 보겠습니다. 🎜$post = new Post($pdo); $items = [ [ 'title' => '这是一篇测试文章111', 'content' => '测试内容' ], [ 'title' => '这是一篇测试文章222', 'content' => '测试内容' ], [ 'title' => '这是一篇测试文章333', 'content' => '测试内容' ], ]; $post->batchInsert($items); $items = $post->selectAll(); print_r($items);
Post
생성자에 전달합니다. 을 클릭한 다음 Post를 순차적으로 호출하여 개체의 메서드를 추가, 삭제, 수정 및 쿼리합니다. 브라우저에서 액세스하고 다음과 같이 결과를 인쇄합니다: 🎜🎜fetchAll
메서드에서 반환된 결과 집합 배열에는 인덱스 매핑과 필드 이름 매핑이 모두 포함되어 있음을 알 수 있습니다. 이는 Post 객체 배열의 경우 다음을 수행할 수 있습니다. 🎜rrreee🎜이 방법으로 반환된 결과는 다음과 같습니다. 🎜🎜🎜🎜자세한 모드 설정은 공식 문서의 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 数据库交互,我们就简单介绍到这里,更多细节可以阅读官方文档,相信通过这几个课程的学习,你已经对 MySQL 数据库的基本使用以及如何在 PHP 中连接数据库并进行增删改查有了初步的认知,从下篇教程开始,我们将结合具体实战项目来开发一个现代的 PHP 项目,将之前的学习到的知识点应用到实战中,并且引入一些现代的 PHP 理念对项目进行管理。
想了解更多相关文章,敬请关注php mysql栏目!
위 내용은 PDO 확장을 통해 MySQL 데이터베이스와 상호 작용하여 추가, 삭제, 수정, 쿼리 구현 및 데이터베이스 트랜잭션을 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!