PDO

PDO概念

PHP和MySQL数据库是“黄金搭档”,但是能和PHP“搭档”的不止MySQL数据库一种,还有mssql、Oracle、SQLServer(局限:只能用微软开发的windows操作系统),每一种数据库都为PHP提供了属于自己的操作数据库的扩展函数!比如,就以连接数据库为例:

MySQL:mysql_connect

mssql:mssql_connect

Oracle:oci_connect

所以,如果PHP要切换不同的数据库,就要切换不同的数据库操作函数:

这样,无论从程序员学习的角度,还是从程序员开发的角度,都很麻烦!

于是PDO就问世了,能把对所有数据库都统一成相同的操作以实现相同的目的

PDO的全称是:PHP Data Object(PHP数据对象)

在新版本的PHP(PHP5以上)中,PHP为用户封装了一套PDO扩展库,专门用来操作不同类型的数据库!

同php需要开启MySQL扩展和GD库扩展一样,以前需要手动的在php.ini中开启PDO扩展,现在默认开启

1
extension=php_pdo.dll  现在已经没有这行,默认开启pdo

我们首先应该学习pdo的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
PDO::__construct(string $dsn[,string $username[,string $password[,array $driver _options]]])

$dsn:data source name数据源名称,其实就是告诉PDO类应该选择哪种类型的数据库,主机名和端口号是什么,选择哪个数据库以及采用什么样的字符编码!
$username:数据库的用户名
$password:数据库的密码
$driver_options:附加驱动信息,暂时用默认

$dbms='mysql';//哪种类型的数据库
$host='localhost';//主机名
$port='3306';//端口号
$dbname='php2017';//哪个数据库
$charset='utf8';//字符编码
$dsn="$dbms:host=$host;port=$port;dbname=$dbname;charset=$charset";

dpo实现-增删改

pdo=newPDO(pdo=new PDO(dsn.user,user,pass);

$sql=“insert into student values(null,‘如来’,‘丽丽’)”;

result=result=pdo->exec($sql)

PDO::exec()在执行一条sql语句,返回受此语句影响的行数

PDO::lastInstertId([string $name=null]),返回最后插入的记录的主键id号

删除和更新,其实就是修改$sql语句就行了!

dpo实现-查

$sql=“select * from student”;

result=result=pdo->query($sql);//执行一条sql查询语句,返回一个PDOStatement类的对象,也叫作结果对象。

rowCount 获取查询结果中的总记录数(总行数)

columnCount 获取查询结果中的总字段数(总列数)

在实际的应用中,获取总记录数的操作比较多

fetch

每次从PDOStatement结果集中,获取一条记录,同时将指针下移,其中该方法有一个参数,可以控制返回值的类型!

PDO::FETCH_ASSOC返回一个关联型的数组,数组的下标就是字段的名字!效果类似于以前的mysql_fetch_assoc

PDO::FETCH_NUM 返回一 个索引数组,效果类似于以前的mysql_fetch_row

PDO::FETCH_BOTH 返回一个关联和索引并存的混合数组,也是缺省值!

PDO::FETCH_OBJ 返回一个对象,其中对象的属性就是字段的值!

FETCH_ASSOC使用最多,利用fetch方法实现对结果集的遍历!

1
2
3
4
$rows=[];
while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
$rows[]=$row;
}

fetchAll 返回一个结果集中所有的记录,形成一个二维数组,比较适合多行多列的结果集的提取

fetchcolumn 每次获取指定的某一列信息中的一条记录,并将指针下移!第一列参数为0,第二列为1,依次类推!例如:var_dump($stmt->fetchcolumn (5))可用于遍历

fetchobject 每次读取一条记录放到一个对象中,并使指针下移!

mysql、pdo的预处理技术

mysql

插入多条数据

1
2
3
insert into php_student values(null,'孙悟空',''XXXX)
insert into php_student values(null,'孙悟空',''XXXX)
.....

每次执行的sql语句的命令和结构都是一样的,只是其中的数据不同,采用传统的执行方式,每次都必须传输整条sql语句,对于MySQL服务器而言,得到的每一次的命令都是全新,都是根据sql语句从0解析并执行!

(实际上我使用以下方法,照样一次执行,这里是做个历史介绍)

1
INSERT INTO `yy_news`(`logo`, `title`, `keywords`, `digest`, `content`, `type`, `addtime`, `recommend`) VALUES ( 'kj', '设备', '45sjksdj', 'Shopset', 'printer',1, 56555448, 1),('kj2', '打备3', '45sjksdj', 'Shopset', 'printer',1, 56555448, 1);

采用预处理机制优化

将前面相同的命令和结构部分发送给MySQL服务器,让MySQL服务器事先进行一次预处理(此时并没有真正的执行sql语句),而为了保证sql语句的结构完整性,在第一次发送sql语句的时候将其中可变的数据部分都用一个数据占位符来表示,比如问号?就是常见的数据占位符!

不带参数的预处理

1
2
3
4
5
6
7
8
9
10
11
1、准备预处理语句
语法形式:prepare 语句名称 from “预处理的sql语句”;
mysql>prepare sql_1 from “select * from student”;
2、执行预处理语句
语法形式:execute 语句名称;
execute sql_1;
mysql>execute sql_1;
3、删除预处理
语法形式:drop prepare 语句名称;
mysql>drop prepare sql_1;
删除之后,就不能再执行预处理语句了!

传参的预处理

以之前不带参数的差不多,只是其中可变的数据部分用一个数据占位符来表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1、准备预处理语句
mysql>prepare sql_1 from “select * from student where id=?”;//?为数据占位符
2、定义参数变量
语法形式:set @变量名 = 值;//@是在MySQL中定义变量的一种语法形式(类比php中的$符号)
mysql>set @id=5;
3、传递参数变量并执行预处理语句
语法形式:execute 语句名称 using 参数变量;//注意:using后面只能是参数变量不能是一个常量!
mysql>execute sql_2 using @id;//想查询其他的记录,只需要修改@id的值就行了
数据占位符不止一个,就按数据占位符的顺序传参就行了
mysql>prepare sql_1 from “select * from student where id=? && score<?”;
mysql>set @id=5;
mysql>set @score=55;
mysql>execute sql_2 using @id,@score;
4、删除预处理
语法形式:drop prepare 语句名称;
(传入变量可以使用增删改)

PDO

基本的原理和mysql预处理都是一样的,只不过就是把前面所有命令行的语法封装成了PDO对象的几个公开的方法而已

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
1、发送预处理语句
调用pdo对象的prepare方法,得到一个PDOStatement结果对象
$pdo=new PDO($dsn.$user,$pass);
$stmt=$pdo->prepare($sql);
2、绑定参数
调用PDOStatement对象中的bindParam方法
$stmt->bindParam(1,$name)//1代表第一个数据占位符
使用bindParam方法绑定的时候不能使用常量直接绑定,因为此时绑定的数据与数据占位符之间是一种引用传递,只能用变量;另外,还可用bindValue,与前者的区别是后者与数据占位符之间是一种值传递
3、执行预处理语句
调用PDOStatement对象中的execute方法,如果执行成功,就返回true,如果执行失败就返回false!
$result=$stmt->execute();
if(result){
echo '执行成功!'
}else{
echo '执行失败!'
}

-------------------------------使用变量 作为数据占位符
$sql="insert into php_student values
(null,:name.:gender,:class_id,.....)"
$stmt=$pdo->prepare($sql);
$name='观世音';
$gender='female';
$class_id=23;
...
$stmt->bindParam(':name',$name);
$stmt->bindParam(':gender',$gender);
$stmt->bindParam(':class_id',$class_id);
....

-----------------------------------使用数组 绑定参数
使用数组以键值对的形式,实现保存数据占位符与值的对应关系
$arr=[
':name' =>$name,
':gender' =>$gender,
':class_id'=>$class_id,
.....
];
$result=$stmt->execute($arr);
if(result){
echo '执行成功!'
}else{
echo '执行失败!'
}

PDO事务处理

student表有id/name/money字段

inster into student (id,name,money)values (1,‘张三’,2000),(2,'李四‘,7000);

线张三找李四借1000,则

update student set money=money-1000 where name=‘李四’;

update student set money=money+1000 where name=‘张三’;

update语句是两个独立的sql语句,MySQL服务器需要独立的执行两次!但是从业务逻辑上看,这两个sql语句必须全部执行成功整个业务逻辑才算是执行成功!只要有一个执行失败,就不符合正常的业务逻辑!

**事务:**将上面的两个操作(两个update)认为是一个事务,其中有任意一条语句执行失败,我们就认为整个事务执行失败!应该回滚“恢复”到最初的状态!**注意:**目前只有InnoDB引擎才支持事务处理

MySQL的事务处理

1
2
3
4
5
6
1、开启事务
语法形式:
start transaction;
mysql>start transaction;
2、执行sql语句
mysql>update student set money=money-1000 where name='李四';

image-20200114113957357image-20200114113957357

**注意:**当我们开启一个事务的时候,我们对sql的操作都发生在内存中,但是没有真正的反馈到数据库磁盘的文件中!可以开启另外一个客户端进行查看数据,数据并没有变化,还是7000。所以,如果此时李四反悔了,可以进行回滚操作!

回滚事务

语法形式:rollback;

1
mysql>rollback;

所谓的事务的回滚,就是恢复到事务开启之前的最原始的状态!

**注意:**回滚操作会自动的关闭一个事务,如果想再次执行事务,需要重新开启事务!

1
2
3
mysql>start transaction;
mysql>update student set money=money-1000 where name='李四';
mysql>update student set money=money+1000 where name='张三';

image-20200114114457458image-20200114114457458

1
2
3
4
5
3、执行事务
语法形式:
commit;
mysql>commit;
到此为止,整个事务处理完毕!通过另外一个客户端进行查看

image-20200114114725386image-20200114114725386

**原理:**普通的执行,之所以是立即执行并生效,因为默认的,MySQL对sql语句的执行是自动提交的!

所以,开启一个事务的本质,就是关闭了以前的自动提交的功能,而是由用户手动提交(利用commit语句)!

开启事务的另一种方式,关闭自动提交的功能关闭!

语法形式为:set autocommit = 0;

mysql>set autocommit = 0;

此时,每次执行sql语句都需要手动的进行提交

PDO的事务处理

基本原理和步骤其实都是一样的,就是调用PDO对象的三个方法:

开启事务:beginTransaction

回滚操作:rollBack

执行操作:commit\

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$pdo->beginTransaction();
$falg=true;
$sql="update student set money=money-1000 where name='李四'";
$result=$pdo->exec($sql);
if($result){
$flag=false;
}
$sql="update student set money=money+1000 where name='张三'";
$result=$pdo->exec($sql);
if($result){
$flag=false;
}
if($flag){
$pdo->commit()
echo '成功';
}else{
$pdo->rollBack();
echo '失败';
}

PDO相关属性(参数)

虽然PDO所有的属性都不是public型,但是还是可以通过两个方法来设置和查看PDO的属性:

setAttribute:设置属性

getAttribute:获取数组的值

常见的属性:

PDO::ATTR_AUTOCOMMIT 自动提交,只有两个值:0或1

0不开启自动提交,手动提交(调用commit方法)

1开启自动提交,自动实现事务处理

PDO::ATTR_CASE:返回结果集的大小写,有三个值可以设置

CASE_LOWER:返回的结果集全部小写!

CASE_UPPER:返回的结果集全部大写!

CASE_NATURAL:返回的结果集正常(默认值,原来数据库中什么样现在返回的还是什么样)

1
2
$pdo=new PDO($dsn,$user,$pass);
$pdo->setAttribute(PDO::ATTRCASE,DO::CASE_UPPER);//设置结果集全部为大写

PDO::ATTR_ERRMODE:返回的错误模式

pdo异常处理

异常的概念:

异常,其实就是错误,不过是在面向对象编程语言中的一种典型的错误处理方式!所谓的异常,就是把错误信息,放到一个 对象中!

但是,异常的发生有一个前提,就是必须保证语法是正确的!

异常的处理:

1
2
3
4
5
6
7
首先要发生异常!

**所谓的发生**,指的是抛出一个异常!

然后是处理异常!

**所谓的处理**,就是一旦异常出现,要得到并处理这个异常,通常有两个阶段组成:监听和捕获!

抛出异常,throw

抛出异常其实就是利用throw关键字来完成的,语法形式为:throw 异常;

注意:

这里的异常,其实就是一个异常对象,这个对象必须是由系统预定义的异常类(类名为Exception)或者其子类实例化出来的!

1
2
3
4
5
6
7
8
9
10
class Goods {
public function getPrice(){
if($price<0){
$e=new Exception($message='价格不能为负值');
throw $e;//抛出异常对象
}else{
echo $price;//正常情况
}
}
}

以上只是抛出了一个异常,并没有进行监听和捕获!(类比抛绣球招亲)

异常监听,try

利用关键字try可以监听一段有可能出现异常的代码内,来监听是否发生了异常!

语法为:try{被监听的代码段;}catch(){}

1
2
3
4
5
6
7
8
9
try{//对代码进行监听
$book=new Goods;
$book=getPrice();
}catch(Exception $e){//捕获异常
echo $e->getmessage(),'<br/>';
echo $e->getCode(),'<br/>';
echo $e->getFile(),'<br/>';
echo $e->getLine(),'<br/>';
}

PDO中处理错误的方式,即设置PDO::ATTR_ERRMODE