数据库抽象层 PDO

13 篇文章 0 订阅

1. PDO 概览;

  • PDO(PHP data object)是一个扩展类库,为 PHP 访问数据库定义了轻量级、一致性的接口
    • 什么是扩展类库?就是在 PHP 基础上进行了一些扩展、并且是以一个类的形式进行扩展。当然它不是 PHP 实现的,扩展类库一般都用 C 语言实现,因为 PHP 是用 C 语言实现的。既然是扩展类库,那使用的时候要把类实例化成对象才能使用
    • 什么是一致性的接口?就是不管操作任何的数据库,都可以使用 PDO 进行操作。只需要掌握标准的 SQL 语句,我们只需要写一套 SQL 语句,就可以操作任何它支持的数据库。它提供了一个数据库访问的抽象层。PDO 就像日常生活中有一个翻译,这个翻译懂很多国语言,只需要请到他,就可以去很多国家玩,不要担心语言的问题。PDO 就充当这样的工作,只要写了标准的 SQL 语句,它会发送给你所操作的各种各样的数据库,都是没问题的。
  • PDO 提供了一个数据访问抽象层,这样,无论你使用了什么数据库,都可以通过一致的函数执行查询和获取数据,大大简化了数据库的操作,并能够屏蔽不同数据库之间的差异。
    • 学会了 PDO,不管工作中遇见什么数据库,都不要再重新学习,只需要学会 PDO 中自带的成员方法就可以操作了。
  • 使用 PDO 可以很方便的进行跨数据库程序的开发,以及不同数据库间的移植,是 PHP 在数据库处理方面的只要发展方向。它支持 MySQL,Oracle,MSSQL 等多种数据库。

2. 创建 PDO 对象与连接相关设置;

# 创建 PDO 对象
$dsn = 'mysql:dbname=testdb;host=127.0.0.1'; // 连接MySQL数据库的DSN(也就是PDO的MySQL驱动)
$user = 'dbuser'; 							 // MySQL数据库的用户名
$password = 'dbpass'; 						 // MySQL数据库的密码
try { 
     $dbh = new PDO($dsn, $user, $password); // 获得 PDO 对象
} catch (PDOException $e) { 
      echo '数据库连接失败: ' . $e->getMessage(); 
}

# 设置 PHP 连接 MySQL 时的客户端字符串和连接字符串集为:
# 注意:开发项目的时候设置字符集不写,因为一旦写了这句,用户连接一次数据库 / 发送一条语句 就执行一次,降低数据库效率
# 只要保证编辑器编码,数据库字符校验集,HTML页面编码三者一致,就不会有乱码。
$pdo->exec(“set names utf8”);
# 或者(两者有区别):
$pdo->query(“set names utf8”);

# PDO 与连接相关的选项

# 异常处理模式:PDO::ATTR_ERRMODE
 - PDO::ERRMODE_SILENT		// 不报、忽略错误(0)
 - PDO::ERRMODE_WARNING		// 以警告的方式报错(1)
 - PDO::ERRMODE_EXCEPTION	// 以异常的方式报错(2)(推荐)

# 自动提交:PDO::ATTR_AUTOCOMMIT
# 会在事务的时候用到,在事务处理的时候首先要关闭自动提交;在事务结束后要开启自动提交
# MySQL 里自动提交是默认开启的
 - 0  //关闭自动提交
 - 1  //开启自动提交

# 结果集返回格式:PDO::ATTR_DEFAULT_FETCH_MODE 
 - PDO::FETCH_ASSOC    	// 返回关联数组
 - PDO::FETCH_NUM       // 索引数组(数字数组)
 - PDO::FETCH_BOTH		// 以上两个
 - PDO::FETCH_OBJ  		// 返回对象格式

  • 实例:pdo.php
<?php

try{
    // $dsn 数据库驱动 "mysql:dbname=数据库;host=数据库地址"
    $dsn = "mysql:dbname=test;host=127.0.0.1";
    // $name 数据库用户名
    $name = "root";
    // $pwd 数据库密码
    $pwd = "asdf";
    
    // 实例化PDO对象
    // 第一个参数 数据库驱动
    // 第二个参数 数据库用户名
    // 第三个参数 数据库密码
    $pdo = new PDO($dsn, $name, $pwd);

    // 设置错误处理模式   推举为异常处理模式
    $pdo -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    // echo PDO::ATTR_ERRMODE;
    // echo PDO::ERRMODE_EXCEPTION;
    // 设置错误处理模式得简写方式
    // $pdo -> setAttribute(3,2);

    // 设置是否关闭自动提交功能
    // $pdo -> setAttribute(PDO::ATTR_AUTOCOMMIT,0);

    // 设置结果集返回的格式
    // $pdo -> setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
    var_dump($pdo);
}catch(PDOException $e){
    echo $e -> getMessage();        // 得到异常的信息
    echo $e -> getFile();           // 得到异常发生的文件
    echo $e -> getLine();           // 得到异常发生的行
    echo $e -> getCode();           // 得到异常码
}

3. 使用 PDO 对象;

  • 调整 PDO 行为
  • 设置错误处理模式
  • 使用 PDO 执行 SQL 语句
  • 实例:pdo.php
<?php

try{
    $dsn = "mysql:dbname=test;host=127.0.0.1";
    $name = "root";
    $pwd = "asdf";
    $pdo = new PDO($dsn,$name,$pwd);
    // setAttribute() 设置PDO的行为属性
    $pdo -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);     
    // getAttribute() 得到PDO行为属性的值
    // echo $pdo -> getAttribute(PDO::ATTR_ERRMODE);  
    $username = "user1";
    $pwd = md5(123456);
    $email = 'user@admin.com';
    // $sql = "INSERT INTO user(username,pwd,email) VALUE('{$username}','{$pwd}','{$email}')";
    // exec() 执行有影响行数的语句
    // 有影响行数的语句一般来说多是增删改,就是对表的结构或内容有修改的语句
    // 返回影响行数
    // $affected = $pdo -> exec($sql);       
    // var_dump($affected);
    
    $sql = "SELECT * FROM user";
    // query() 执行有结果集的语句,多用于执行查询,对表的结构和内容没有任何影响
    $stmt = $pdo -> query($sql);

    foreach($stmt as $v){
        echo "{$v['id']}-{$v['username']}-{$v['pwd']}-{$v['email']}-<br />";
    }
}catch(PDOException $e){
    echo $e -> getMessage();
}

4. PDO 预处理;

  • 概念
    • PDO 预处理对象,更安全,更高效
    • 在使用 PHP 的 MySQL 执行 SQL 语句,这样的语句会有一个问题:每次发送语句,数据库都会重新编译一下
    • 这里有一个脚本,要执行删除用户的操作,语句为DELETE FROM user WHERE id = $_GET['id'],然后就会把这条语句发送到数据库,让这条语句编译执行。
    • 如果在编译的过程中,这个 id 不经过特殊处理的话,很可能产生安全性问题,比如说 SQL 注入。
    • 举个例子, http://www.123.com/user.php?id=5,这虽然是一个正常的 url,但是它在地址栏里,用户是可以改变它的,用户把它改了下,变成了 http://www.123.com/user.php?id=5||1=1,那之前的 SQL 语句通过传值就变成了DELETE FROM user WHERE id = 5||1=1
    • 然后这条 SQL 语句发送给数据库执行,然后表里所有的数据库就都被删除了。
    • 这就是原来的处理方式,它并不安全。
    • 如果使用 PDO 的预处理语句,它就不会发生以上的情况。
    • 同样是上面的 SQL 语句,DELETE FROM user WHERE id =?,这里的“?”是一个语法叫占位符。这句叫准备语句,会先发送给数据库进行编译。
    • 编译的过程中,它只编译一部分,就是DELETE FROM user WHERE id =,问号部分是等待传参数。
    • 如果学过编译原理那就会知道,计算机在运行的时候,程序在运行的时候,是有一定的逻辑运算的,但是这些逻辑运算需要计算机去认识的。而我们的语言是高级语言,计算机是不认识的。
    • 而编译的过程就是让计算机去认识我们所写的一些代码,实际就是一个翻译的过程。
    • 现在,我们只翻译了这一部分DELETE FROM user WHERE id =,那计算机就认识了。
    • 问号的部分等待传参。如果问号的部分传过来了,不经过编译,里面有任何的逻辑,计算机都不会认识,会把它当做一个普通的字符串去处理。
    • 就相当于你看到一个甲骨文。你不明白它是什么意思,你只知道它是一种文字。
    • 预处理就是这样一个过程。后传的参数不会去翻译,再多的逻辑都不会被执行,这样就比较安全。
    • PDO 预处理还有一个好处,我们的语句只编译一次。就像DELETE FROM user WHERE id =这部分编译好了,之后在执行相同的语句,就不会重新编译了。
    • 在软件开发中,编译的过程是一个最耗时的过程,效率最低。
  • PDOStatement 对象的方法
# 方法

fetch()             返回结果集的下一行,结果指针下移,到头返回false 
fetchAll()        	通过一次调用返回所有结果,结果是以数组形式保存
execute() 	   		负责执行一个准备好了的预处理语句 
rowCount()    		返回使用增、删、改、查操作语句后受影响的行总
bindParam()  		将参数绑定到相应的查询占位符上
bindColumn()  		将查询结果的字段绑定变量

  • 准备语句
# 得到 PDO 预处理对象的方法:
$sql = "select * from user order by id";
$sth = $pdo->prepare($sql);

# 以上代码中的 $sth 即为预处理对象
# 在 PDO 中参数式的 SQL 语句有两种(预处理 SQL ):
insert into stu(id,name) value(?,?); 		// ?号式(适合参数少的)	
insert into stu(id,name) value(:id,:name);	// 别名式(适合参数多的)

  • 绑定参数
1.占位符方式
$stmt->bindParam(1, $name, PDO::PARAM_STR); 

2.别名方式
$stmt->bindParam(':name', $name); 

  • 执行预处理方式
1.占位符方式
$query = "INSERT INTO contactInfo (name, address, phone) VALUES (?, ?, ?)"; 
$stmt = $dbh->prepare($query); 

// 传递一个数组为预处理查询中的问号参数绑定值,并执行一次。 
$stmt->execute(["张三", "上海", "13111111111"]);   

// 再次传递一个数组为预处理查询中的问号参数绑定值,并执行第二次插入数据。 
$stmt->execute(["李四", "北京", "133333333333"]);

2.别名方式
$query = "INSERT INTO contactInfo (name, address) VALUES (:name, :address)"; 

// 调用 PDO 对象中的 prepare() 方法准备查询,使用命名参数 
$stmt = $dbh->prepare($query); 

// 传递一个数组为预处理查询中的命名参数绑定值,并执行一次。
 $stmt->execute([":name"=>"张三",":address"=>"上海"]);  
  
// 再次传递一个数组为预处理查询中的命名参数绑定值,并执行第二次插入数据。 
$stmt->execute([":name"=>"李四",":address"=>"北京"]); 

  • 预处理查询
$query = "SELECT uid, name, phone, email FROM contactInfo WHERE xx = yy 
$stmt = $dbh->prepare($query);   	// 准备声明好的一个查询 

$stmt->execute();          	// 执行准备好的查询 
$stmt->bindColumn(1, $uid);     	// 通过列位置偏移数绑定变量
$stmt->bindColumn(2, $name);   		// 通过列位置偏移数绑定变量$name 
$stmt->bindColumn('phone', $phone); // 绑定列名称到变量$phone上 
$stmt->bindColumn('email', $email);	// 绑定列名称到变量$email上 

while ($stmt->fetch(PDO::FETCH_BOTH)) { 
	echo $uid."\t".$name."\t".$phone."\t".$email."\n"; 
} 

  • 实例: pdo.php
<?php

try{
    $dsn = "mysql:host=127.0.0.1;dbname=test";
    $name = "root";
    $pwd = "asdf";
    $pdo = new PDO($dsn, $name, $pwd);
    $pdo -> setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
    echo $e -> getMessage();
}
  • stmt.php(占位符方式)
<?php
include("pdo.php");
try{
    $sql = "INSERT INTO user(username,pwd,email) VALUE(?,?,?)"; //  占位符方式,“?” 是占位符
    $stmt = $pdo -> prepare($sql);  // prepare() 准备语句,这个方法是先对 SQL 语句进行编译,等待传参
    // var_dump($stmt); // 返回一个 pdo 预处理对象,不是手工识别化出来的
/*
    // bindParam() 绑定参数
    // 第一个参数:第几个占位符
    // 第二个参数:要绑定的变量(必须是变量,不能是具体的值)
    // 第三个参数:变量的数据类型(一般不用写)
    $stmt -> bindParam(1, $username);
    $stmt -> bindParam(2, $pwd);
    $stmt -> bindParam(3, $email);
	
	// 给变量赋值
    $username = "user3";
    $pwd = md5(123456);
    $email = "user3@admin.com";
    
    // execute()  执行预处理语句(最终执行 SQL 语句)
    $return = $stmt -> execute();
    // var_dump($return);	// 插入成功返回 true

    $username = "user4";
    $pwd = md5(123456);
    $email = "user4@admin.com";
    $stmt -> execute();
 */
    $pwd = md5(123456);
    //execute() 可以用这个方法直接传参,不须要使用绑定参数方法
    $stmt -> execute(["user5", $pwd, "user5@admin.com"]);	// 必须是**索引数组**
}catch(PDOException $e){
    echo $e -> getMessage();
}

  • other.php(别名方式,推荐)
<?php
include("pdo.php");
try{
    $sql = "INSERT INTO user(username,pwd,email) VALUE(:username,:pwd,:email)";	// 别名,开头加冒号
    $stmt = $pdo -> prepare($sql);

    /*
    $stmt -> bindParam(":username", $username);
    $stmt -> bindParam(":pwd", $pwd);
    $stmt -> bindParam(":email", $email);

    $username = "user6";
    $pwd = md5(123456);
    $email = "user6@admin.com";

    $stmt -> execute();
     */
    $pwd = md5(123456);
    // 必须是**关联数组**,下标必须和别名一致
    $arr = ["username" => "user7","pwd" => $pwd,"email" => "user7@admin.com"];	
    $stmt -> execute($arr);

}catch(PDOException $e){
    echo $e -> getMessage();
}

  • select.php
<?php
include("pdo.php");
try{
    $sql = "SELECT * FROM user WHERE id > :id";
    $stmt = $pdo -> prepare($sql);
    $stmt -> execute($_GET);

    //$user = $stmt -> fetch(PDO::FETCH_ASSOC);     // fetch() 提取一条数据时使用的方法 
    //$users = $stmt -> fetchAll(PDO::FETCH_ASSOC); // fetchAll() 提取多条数据时使用的方
    //var_dump($stmt -> rowCount());    // rowCount() 可以得到查询时结果集的数量或增删改时影响行数
    //
    $stmt -> bindColumn(1, $id); //bindColumn() 把查询的结果集字段绑定到一个变量上
    $stmt -> bindColumn(2, $username);
    $stmt -> bindColumn("pwd", $pwd);
    $stmt -> bindColumn("email", $email);

    while($stmt -> fetch(PDO::FETCH_ASSOC)){
        echo "{$id}-{$username}-{$pwd}-{$email}-<br />"; 
    }
   
}catch(PDOException $e){
    echo $e -> getMessage();
}

5. PDO 事务处理。

  • MySQL 的事务处理
# 事务:将多条 SQL 操作(增删改)作为一个操作单元,要么都成功,要么都失败。
# MySQL 对事务的支持:
# 被操作的表必须是 innoDB 类型的表(支持事务)
# MySQL 常用的表类型:MyISAM (非事务)增删改速度快、InnodB(事务型)安全性高
  • 构建事务处理应用程序
# 开启一次事务:
$pdo->beginTransaction();

# 提交一次事务:
$pdo->commit();

# 回滚一次事务:
$pdo->rollback();

# 注意如下设置:
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT,0);
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT,1);

  • 实例:pdo.php
<?php
try{
    $dsn = "mysql:host=127.0.0.1;dbname=test";
    $name ="root";
    $pwd = "asdf";
    $pdo = new PDO($dsn, $name, $pwd);
    $pdo -> setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
    echo $e -> getMessage();
}

  • cash.php
<?php
include("pdo.php");
try{
    // 第一步:关闭自动提交
    $pdo -> setAttribute(PDO::ATTR_AUTOCOMMIT,0);
    // 第二步:开启事务
    $pdo -> beginTransaction();

    // 第三步:执行子流程
    // 如果子流程有失败的则整个事务都失败
    // 如果所有的子流程都成功则事务成功
    $sql = "UPDATE cash SET money = money - 50 WHERE username = 'zhangsan'";
    $affectedRow = $pdo -> exec($sql);
    if(!$affectedRow){
        throw new PDOException("转出失败!");
    }

    $sql = "UPDATE cash SET money = money + 50 WHERE username = 'lisi'";
    $affectedRow = $pdo -> exec($sql);
    if(!$affectedRow){
        throw new PDOException("转入失败!");
    }

    //事务成功:提交事务
    $pdo -> commit();
    echo "汇款成功!";
}catch(PDOException $e){
    echo $e -> getMessage();
    //事务失败:回滚事务
    $pdo -> rollback();
}

//开启自动提交
$pdo -> setAttribute(PDO::ATTR_AUTOCOMMIT,1);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值