1. 程式人生 > 實用技巧 >MySQL資料庫之PDO擴充套件

MySQL資料庫之PDO擴充套件

PDO概述

  • 連線資料庫方式

    • mysql擴充套件 (這種方式php7已經淘汰)
    • mysqli擴充套件
    • PDO擴充套件
  • PDO介紹

    • PDO(PHP Data Object)擴充套件為PHP訪問各種資料庫提供了一個輕量級,一致性的介面
    • 無論訪問什麼資料庫,都可以通過一致性的介面去操作
  • 開啟PDO擴充套件

    • 開啟PDO連線MySQL擴充套件
    • extension=php_pdo_mysql.dll
  • PDO核心類

    • PDO 表示PHP和資料庫之間的一個連線
    • PDOStatement
      • 表示執行資料查詢語句(select ,show)後的相關結果集
      • 預處理物件
    • PDOException
      表示PDO的異常

例項化PDO物件

  • 語法
    • __construct($dsn,使用者名稱,密碼)

DSN

  • 概念

    • DSN:data source name
    • 資料來源名稱,包含的是連線資料庫的資訊
  • 格式

    • $dsn=資料庫型別:host=主機地址;port=埠號;dbname=資料庫名稱;charset=字符集
  • 資料庫型別

    • MySQL資料庫 => mysql:
    • oracle資料庫 => oci:
    • SQL Server => sqlsrv:

例項化PDO

  • 概念
    • 例項化PDO的過程就是連線資料庫的過程
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
var_dump($pdo);
?>

引數省略

  • 引數省略
    • 如果連線的是本地資料庫,host可以省略
    • 如果使用的是3306埠,port可以省略
    • charset也省略,如果省略,使用的是預設字元編碼
    • dbname也可以省略,如果省略就沒有選擇資料庫
    • host、port、dbname、charset不區分大小寫,沒有先後順序
    • 驅動名稱不能省略,冒號不能省略(因為冒號是驅動名組成部分),資料庫驅動只能小寫
<?php
$dsn='mysql:';
$pdo=new PDO($dsn,'root','');
var_dump($pdo); 
?>

使用PDO

執行資料操作語句

  • 語法
    • $pdo->exec($sql)
    • 執行資料增、刪、改語句
    • 執行成功返回受影響的記錄數
    • 如果SQL語句錯誤返回false

執行增加

<?php
$dsn='mysql:host=localhost;port=3306;dbname=sel;charset=utf8';
$pdo=new PDO($dsn,'root','');
if($pdo->exec("insert into news values (null,'基本知識','第6章',unix_timestamp())")){
    echo '自動增長的編號是:'.$pdo->lastInsertId(),'<br>';
} 
?>

執行刪除

<?php
$dsn='mysql:host=localhost;port=3306;dbname=sel;charset=utf8';
$pdo=new PDO($dsn,'root','');
if($pdo->exec('delete from news where id=13')){
    echo 'delete success ^_^';
}
?>

執行修改

<?php
$dsn='mysql:host=localhost;port=3306;dbname=sel;charset=utf8';
$pdo=new PDO($dsn,'root','');
if($pdo->exec("update news set title='準備知識' where id in (3,4)")){
    echo 'update success ^_^';
}
?>

優化寫法

<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=sel;charset=utf8';
$pdo= new PDO($dsn,'root','');

$sql= "update news set title='基本知識' where id in (3,4)";
$rs= $pdo->exec($sql);
if($rs){
    echo 'SQL語句執行成功<br>';
    if(substr($sql, 0,6)=='insert')
        echo '自動增長的編號是:'.$pdo->lastInsertId (),'<br>';
    else
        echo '受到影響的記錄數是:'.$rs,'<br>';
}elseif($rs===0){
    echo '資料沒有變化<br>';
}elseif($rs===false){
    echo 'SQL語句執行失敗<br>';
    echo '錯誤編號:'.$pdo->errorCode(),'<br>';
    echo '錯誤資訊:'.$pdo->errorInfo()[2];
}
?>

執行資料查詢語句

  • 語法

    • $pdo->query($sql)
    • 返回的是PDOStatement物件
  • 獲取二維陣列

    • $rs=$stmt->fetchAll(); 預設返回關聯和索引陣列
    • $rs=$stmt->fetchAll(PDO::FETCH_BOTH); 返回關聯和索引陣列
    • $rs=$stmt->fetchAll(PDO::FETCH_NUM); 返回索引陣列
    • $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); 返回關聯陣列
    • $rs=$stmt->fetchAll(PDO::FETCH_OBJ); 返回物件陣列
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetchAll();
var_dump($rs);
?>
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetchAll(PDO::FETCH_BOTH);
var_dump($rs);
?>
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetchAll(PDO::FETCH_NUM);
var_dump($rs);
?>
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($rs);
?>
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetchAll(PDO::FETCH_OBJ);
var_dump($rs);
?>
  • 獲取一維陣列
    • $rs=$stmt->fetch(); 關聯和索引陣列
    • $rs=$stmt->fetch(PDO::FETCH_NUM); 索引陣列
    • $row=$stmt->fetch(PDO::FETCH_ASSOC) 關聯陣列
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetch(); 
var_dump($rs);
?>
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
$rs=$stmt->fetch(PDO::FETCH_NUM); 
var_dump($rs);
?>
# 通過while迴圈獲取所有資料
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
    $rs[]=$row;
}
var_dump($rs);
?>
  • 匹配列
    • 匹配當前行的第n列,列的編號從0開始,匹配完畢後指標下移一條
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
for($i=0; $i<$stmt->columnCount(); $i++){
    echo $stmt->fetchColumn($i).'<br>';
}
?>
  • 總行數與總列數
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
echo '總行數:'.$stmt->rowCount(),'<br>';
echo '總列數:'.$stmt->columnCount();
?>
  • 遍歷PDOStatement物件
    • PDOStatement物件是有迭代器的
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$stmt=$pdo->query('select * from products');
foreach($stmt as $row){
    echo $row['proname'],'-',$row['proprice'],'<br>';
}
?>

PDO操作事務

  • 概念

    • 事務:是一個整體,要麼一起執行,要麼一起回滾
    • 事務的特性:原子性,一致性,隔離性,永久性
    • 需要將多個SQL語句作為一個整體執行,就需要使用到事務
  • MySQL 事務語法

    • start transaction 開啟事務
    • begin 開啟事務
    • commit 提交事務
    • rollback 回滾事務
  • PDO 事務語法

    • $pdo->beginTransaction() 開啟事務
    • $pdo->commit () 提交事務
    • $pdo->rollBack() 回滾事務
# 建立測試資料表
<?php
$dsn= 'mysql:host=localhost;port=3306;dbname=data;charset=utf8';
$pdo= new PDO($dsn,'root','');
$pdo->beginTransaction();
$flag0= $pdo->exec("delete from bank");
$flag1= $pdo->exec("drop table bank");
$flag2= $pdo->exec("create table bank(cardid char(4) primary key comment '卡號',balance decimal(10,2) not null comment '餘額')engine=innodb charset=utf8 comment '銀行卡號表'");
$flag3= $pdo->exec("insert into bank values ('1001',1000),('1002',1)");
if($flag2==0 && $flag3){
    $pdo->commit();    
    echo '資料表建立成功!';
}else{ 
    $pdo->rollBack();
    echo '資料表建立失敗!';
}
?>
# PDO操作事務
<body>
<?php
if(!empty($_POST)){
    $dsn='mysql:dbname=sel;charset=utf8';
    $pdo=new PDO($dsn,'root','');
    $out=$_POST['card_out'];    //轉出卡號
    $in=$_POST['card_in'];      //注入卡號
    $money=$_POST['money'];     //金額
    $pdo->beginTransaction();   //開啟事務
    //轉賬
    $flag1=$pdo->exec("update bank set balance=balance-$money where cardid='$out'");
    $flag2=$pdo->exec("update bank set balance=balance+$money where cardid='$in'");
    //檢視轉出的賬號是否大於0,大於0返回true,否則返回false
    $stmt=$pdo->query("select balance from bank where cardid='$out'"); 
    $flag3=$stmt->fetchColumn()>=0?1:0;
    if($flag1 && $flag2 && $flag3){
        $pdo->commit ();    //提交事務
        echo '轉賬成功';
    }
    else{
        $pdo->rollBack ();  //回滾事務
        echo '轉賬失敗';
    }
}
?>
<form action="" method="post">
    轉出卡號: <input type="text" name="card_out" id=""> <br>
    轉入卡號: <input type="text" name="card_in" id=""> <br>
    金額:<input type="text" name="money" id=""> <br>
    <input type="submit" value="提交">
</form>
</body>

PDO操作預處理

  • MySQL中的預處理

    • 預處理好處:編譯一次多次執行,用來解決一條SQL語句多次執行的問題,提高了執行效率
    • 預處理語句
      • prepare 預處理名字 from 'sql語句'
    • 執行預處理
      • execute 預處理名字 [using 變數]
  • PDO中的預處理

    • 位置佔位符 insert into bank values (?,?)
    • 引數佔位符 insert into bank values (:p1,:p2)
    • $stmt->bindParam()$stmt->bindValue()的區別
      • $stmt->bindParam() 中的值只能是變數
      • $stmt->bindValue() 中的值能是變數也可以為值
    • 預處理的好處
      • 提高執行效率
      • 提高安全性
# 位置佔位符
<?php
    $dsn= 'mysql:host=localhost;port=3306;dbname=sel;charset=utf8';
    $pdo= new PDO($dsn,'root','');
    //建立預處理物件
    $stmt=$pdo->prepare("insert into bank values (?,?)");   //?是佔位符
    //執行預處理
    $cards=[
        ['1003',500],
        ['1004',100]
    ];
    foreach($cards as $card){
        //繫結引數,並執行預處理,
        //方法一:
        /*
        $stmt->bindParam(1, $card[0]);  //佔位符的位置從1開始
        $stmt->bindParam(2, $card[1]);
        $stmt->execute();               //執行預處理
         */
        //方法二:
        /*
        $stmt->bindValue(1, $card[0]);
        $stmt->bindValue(2, $card[1]);
        $stmt->execute();
         */
        //方法三:如果佔位符的順序和陣列的順序一致,可以直接傳遞陣列
        $stmt->execute($card);  
    }
    echo '資料處理完成';
?>
# 引數佔位符
<?php
    $dsn= 'mysql:host=localhost;port=3306;dbname=sel;charset=utf8';
    $pdo= new PDO($dsn,'root','');
    //建立預處理物件
    $stmt=$pdo->prepare("insert into bank values (:p1,:p2)");   //:p1,:p2是引數佔位符
    //執行預處理
    $cards=[
        ['p1'=>'1005','p2'=>500],
        ['p1'=>'1006','p2'=>1000]
    ];
    foreach($cards as $card){
        //方法一:
        /*
        $stmt->bindParam(':p1', $card['p1']);
        $stmt->bindParam(':p2', $card['p2']);
        $stmt->execute();
        */
        //方法二:但陣列的下標和引數名一致的時候就可以直接傳遞關聯陣列
        $stmt->execute($card);
    }
    echo '資料處理完成';
?>

PDO異常處理

  • 概念
    • PDOException是PDO的異常類
    • 例項化PDO會自動丟擲異常
    • 其他操作不會丟擲異常,需要設定PDO的異常模式
    • PDO異常模式
      • PDO::ERRMODE_EXCEPTION 丟擲異常
      • PDO::ERRMODE_SILENT 中斷
      • PDO::ERRMODE_WARNING 警告
<?php
try{
    $dsn='mysql:dbname=data;charset=utf8';
    $pdo=new PDO($dsn,'root','');
    //這是PDO錯誤模式屬性,PDO自動丟擲異常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->query('select * from newsssssss');  //自動丟擲異常
} catch (PDOException $ex) {
    echo '錯誤資訊:'.$ex->getMessage(),'<br>';
    echo '錯誤檔案:'.$ex->getFile(),'<br>';
    echo '錯誤行號:'.$ex->getLine();
}
?>

單例模式封裝MySQL-PDO

  • 分析
    • 單例模式
    • 初始化引數
    • 連線資料庫
    • 執行增刪改
    • 執行查詢
      • 返回二維陣列
      • 返回一維陣列
      • 返回一行一列

第一部分:單例、初始化引數、例項化PDO

<?php
class MyPDO{
    private $type;      //資料庫類別
    private $host;      //主機地址
    private $port;      //埠號
    private $dbname;    //資料庫名
    private $charset;   //字符集
    private $user;      //使用者名稱
    private $pwd;       //密碼
    private $pdo;       //儲存PDO物件
    private static $instance;
    private function __construct($param) {
        $this->initParam($param);
        $this->initPDO();
    }
    private function __clone() {
    }
    public static function getInstance($param=array()){
        if(!self::$instance instanceof self)
            self::$instance=new self($param);
        return self::$instance;
    }
    //初始化引數
    private function initParam($param){
        $this->type=$param['type']??'mysql';
        $this->host=$param['host']??'127.0.0.1';
        $this->port=$param['port']??'3306';
        $this->dbname=$param['dbname']??'data';
        $this->charset=$param['charset']??'utf8';
        $this->user=$param['user']??'root';
        $this->pwd=$param['pwd']??'root';
    }
    //初始化PDO
    private function initPDO(){
        try{
            $dsn="{$this->type}:host={$this->host};port={$this->port};dbname={$this->dbname};charset={$this->charset}";
            $this->pdo=new PDO($dsn, $this->user, $this->pwd);
        } catch (PDOException $ex) {
            echo '錯誤編號:'.$ex->getCode(),'<br>';
            echo '錯誤行號:'.$ex->getLine(),'<br>';
            echo '錯誤檔案:'.$ex->getFile(),'<br>';
            echo '錯誤資訊:'.$ex->getMessage(),'<br>';
            exit;
        }
    }
}
//測試
$param=array(
);
$mypdo= MyPDO::getInstance($param);
var_dump($mypdo);
?>

第二部分:資料操作部分

<?php
class MyPDO{
    private $type;      //資料庫類別
    private $host;      //主機地址
    private $port;      //埠號
    private $dbname;    //資料庫名
    private $charset;   //字符集
    private $user;      //使用者名稱
    private $pwd;       //密碼
    private $pdo;       //儲存PDO物件
    private static $instance;
    private function __construct($param) {
        $this->initParam($param);
        $this->initPDO();
        $this->initException();
    }
    private function __clone() {
    }
    public static function getInstance($param=array()){
        if(!self::$instance instanceof self)
            self::$instance=new self($param);
        return self::$instance;
    }
    //初始化引數
    private function initParam($param){
        $this->type=$param['type']??'mysql';
        $this->host=$param['host']??'127.0.0.1';
        $this->port=$param['port']??'3306';
        $this->dbname=$param['dbname']??'data';
        $this->charset=$param['charset']??'utf8';
        $this->user=$param['user']??'root';
        $this->pwd=$param['pwd']??'root';
    }
    //初始化PDO
    private function initPDO(){
        try{
            $dsn="{$this->type}:host={$this->host};port={$this->port};dbname={$this->dbname};charset={$this->charset}";
            $this->pdo=new PDO($dsn, $this->user, $this->pwd);
        } catch (PDOException $ex) {
            $this->showException($ex);
            exit;
        }
    }
    
    //顯示異常
    private function showException($ex,$sql=''){
        if($sql!=''){
            echo 'SQL語句執行失敗<br>';
            echo '錯誤的SQL語句是:'.$sql,'<br>';
        }
        echo '錯誤編號:'.$ex->getCode(),'<br>';
        echo '錯誤行號:'.$ex->getLine(),'<br>';
        echo '錯誤檔案:'.$ex->getFile(),'<br>';
        echo '錯誤資訊:'.$ex->getMessage(),'<br>';
    }
    //設定異常模式
    private function initException(){
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
    }

    //執行增、刪、改操作
    public function exec($sql){
        try{
            return $this->pdo->exec($sql);
        } catch (PDOException $ex) {
            $this->showException($ex, $sql);
            exit;
        }
    }
    //獲取自動增長的編號
    public function lastInsertId(){
        return $this->pdo->lastInsertId();
    }
}
//測試
$param=array(
   
);
$mypdo= MyPDO::getInstance($param);
//echo $mypdo->exec('delete from news where id=6');
if($mypdo->exec("insert into news values (null,'11','1111',unix_timestamp())"))
    echo '自動增長的編號是:'.$mypdo->lastInsertId ();
?>

第三部分:資料查詢部分

<?php
class MyPDO{
   ...
    
    //判斷匹配的型別
    private function fetchType($type){
        switch ($type){
            case 'num':
                return PDO::FETCH_NUM;
            case 'both':
                return PDO::FETCH_BOTH;
            case 'obj':
                return PDO::FETCH_OBJ;
            default:
                 return PDO::FETCH_ASSOC;
        }
    }
    //獲取所有資料 ,返回二維陣列
    public function fetchAll($sql,$type='assoc'){
        try{
            $stmt=$this->pdo->query($sql);  //獲取PDOStatement物件
            $type= $this->fetchType($type); //獲取匹配方法
            return $stmt->fetchAll($type);
        } catch (Exception $ex) {
            $this->showException($ex, $sql);
        }
    }
    //獲取一維陣列
    public function fetchRow($sql,$type='assoc'){
        try{
            $stmt=$this->pdo->query($sql);  //獲取PDOStatement物件
            $type= $this->fetchType($type); //獲取匹配方法
            return $stmt->fetch($type);
        } catch (Exception $ex) {
            $this->showException($ex, $sql);
            exit;
        }
    }
    //返回一行一列
    public function fetchColumn($sql){
        try{
             $stmt=$this->pdo->query($sql);
            return $stmt->fetchColumn();
        } catch (Exception $ex) {
            $this->showException($ex, $sql);
            exit;
        }
        
    }
    
}
//測試
$param=array(
   
);
$mypdo= MyPDO::getInstance($param);
//echo $mypdo->exec('delete from news where id=6');
/*
if($mypdo->exec("insert into news values (null,'11','1111',unix_timestamp())"))
    echo '自動增長的編號是:'.$mypdo->lastInsertId ();
 */

//$list=$mypdo->fetchAll('select * from news');
//$list=$mypdo->fetchRow('select * from news where id=1');
$list=$mypdo->fetchColumn('select count(*) from news');
echo '<pre>';
var_dump($list);
?>