1. 程式人生 > >關於PDO--資料庫抽象層

關於PDO--資料庫抽象層

1.什麼是PDO?

PDO(PHP Data Object),資料庫訪問抽象層,統一各種資料庫的訪問介面。
PDO的特性:

  1. 編碼一致性
  2. 靈活性
  3. 高效能
  4. 面向物件特性

2.PDO的安裝與配置

  1. 配置PHP配置檔案,開啟相應擴充套件
    extension=php_pdo_mysql.dll
  2. 通過檢視phpinfo可以看到PDO擴充套件的詳細資訊

這裡寫圖片描述

3.PDO連線到資料庫

  1. 通過引數形式連線資料庫
  2. 通過URI形式連線資料庫
  3. 通過配置檔案形式連線資料庫

3.1通過引數形式連線資料庫

<?php
    try {
        $dsn = "mysql:host=localhost;dbname=egovacms_db2"
; $username = "root"; $password = ""; $pdo = new PDO($dsn, $username, $password); }catch(PDOException $e) { echo $e->getMessage(); } ?>

3.2通過uri的形式連線資料庫
1.pdo_connect.php

<?php
    //通過uri的形式連線資料庫
    try {
        $dsn = 'uri:file://D:\wamp\www\temp\phptest\dsn.txt'
; $username = 'root'; $password = ''; $pdo = new PDO($dsn, $username, $password); var_dump($pdo); }catch(PDOException $e) { echo $e->getMessage(); } ?>

2.dsn.txt

mysql:host=localhost;dbname=egovacms_db2

3.3使用配置檔案連線資料庫
1.開啟php.ini配置檔案,在任意位置新增,我在[PHP]下面添加了這樣一行程式碼

pdo.dsn.zjp = "mysql:host=localhost;dbname=egovacms_db2";

2.重啟伺服器
3.測試連線

<?php
    try {
        $dsn = 'zjp';
        $username = 'root';
        $password = '';
        $pdo = new PDO($dsn, $username, $password);
        var_dump($pdo);
    }catch(PDOException $e) {
        echo $e->getMessage();
    } 
?>

4.PDO方法

4.1exec()執行建表語句

<?php
try {
    $dsn = 'mysql:host=localhost;dbname=testpdo';
    $username = 'root';
    $password = '';
    $pdo = new PDO($dsn, $username, $password);
    $sql = <<<EOF
        CREATE TABLE IF NOT EXISTS user(
            id INT UNSIGNED AUTO_INCREMENT KEY,
            uesrname VARCHAR(20) NOT NULL UNIQUE,
            password VARCHAR(32) NOT NULL,
            email VARCHAR(32) NOT NULL
        );
EOF;
    $res = $pdo->exec($sql);
    var_dump($res);
}catch(PDOException $e) {
    echo $e->getMessage();
} 
?>

執行這段程式碼後會發現數據庫中建立了user表,螢幕輸入int(0);
4.2exec()執行插入操作

$sql = 'INSERT user(username, password, email) VALUES("zjp", "'.md5('zjp').'", "[email protected]")';
$res = $pdo->exec($sql);
var_dump($res);
echo "最後插入的ID號:".$pdo->lastInsertId();

會發現user表中多出了一條資料,螢幕輸入int(1)
PDO還提供了檢視最後插入那條資料的ID號。lastInsertId();
同樣的update和delete也可以這樣來執行,這裡就不贅述了。
4.3errorCode()和errorInfo()方法

<?php
    //user12表示不存在的
    $sql = "delete * from user12 where id = 1";
    $res = $pdo->exec($sql);
    if($res==false) {
        //輸出錯誤資訊
        echo 'SQLSTATE值:'.$pdo->errorCode();
        echo '<br/>';
        //errorInfo()返回錯誤資訊的陣列
        //0=>SQLSTATE,1=>CODE,2=>INFO
        print_r($pdo->errorInfo());
    }
?>

4.4query()方法查詢資料
因為exec()是不能執行查詢操作的,所以出現了query()方法。

//查詢資料庫中的資料,執行成功後返回一個pdostatement物件
$sql = 'SELECT * FROM user';
$stmt = $pdo->query($sql);
//取出資料使用foreach()遍歷
foreach($stmt as $row) {
    echo '編號:'.$row['id'].'<br/>';
    echo '使用者名稱:'.$row['username'].'<br/>';
    echo '郵箱:'.$row['email'].'<br/>';
    echo '<hr>';
}
//這樣就輸出了查詢到的資料了

我們可以嘗試,使用query()同樣可以執行insert,update等語句,但一般情況下我們還是使用query()來進行查詢操作,使用exec()來進行增刪改操作。
4.5prepare()預處理和execute()執行

//需要執行的SQL語句
$sql = 'SELETE * FROM user';
//預處理SQL語句
$stmt = $pdo->prepare($sql);
//執行SQL語句
$res = $stmt->execute();
//如果查詢到結果就遍歷出來
if($res) {
    whild($row = $stmt->fetch()) {
        print_r($row);
        echo '<br/>';
    }
    //也可以使用fetchAll()方法來獲取全部資料
    print_r($stmt->fetchAll());
}

我獲得的資料是關聯加索引形式的陣列:
這裡寫圖片描述
一般情況下在專案中,我們使用的是關聯陣列,那麼如何只獲取關聯陣列呢。
PDO為我們提供了方法。返回資料的形式有很多哦。

$stmt->fetch(PDO::FETCH_ASSOC)

同樣的在fetchAll()方法中也可以使用。
4.6getAttribute()和setAttribute()

echo '自動提交:'.$pdo->getAttribute(PDO::ATTR_AUTOCOMMIT);

可以看到輸出結果:自動提交:1。預設是開啟自動提交的
同樣可以使用setAttribute()來設定資料庫的屬性

$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
echo '自動提交:'.$pdo->getAttribute(PDO::ATTR_AUTOCOMMIT);

這是就會輸出:自動提交:0。
4.7使用prepare()和execute()防止SQL注入
login.html

<form action='doLogin.php' method='post'>
<input type='text' name='username' value='' />
<input type='password' name='password' value='' />
<input type='submit' value='Login' />
</form>

doLogin.php

<?php 
header('Content-Type:text/html;charset=utf-8');
$username = $_POST['username'];
$password = $_POST['password'];
try {
    $pdo = new PDO('mysql:host=localhost;dbname=testpdo', 'root', '');
    //第一種佔位符
    $sql = "SELECT * FROM user WHERE username=:username AND password=:password";
    //第二種佔位符
    $sql = "SELECT * FROM user WHERE username=? AND password=?";
    $stmt = $pdo->prepare($sql);
    //第一種傳參
    $stmt = execute(array(":username"=>$username, "password"=>$password));
    //第二種傳參
    $stmt = execute(array($username, $password));
    echo $stmt->rowCount();
}catcch(PDOException $e) {
    echo $e->getMessage();
}
?>

4.8bindParam()方法繫結引數

//第一種繫結引數的方法
$sql = "SELECT * FROM user WHERE username=:username AND password=:password";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$username = 'zjp';
$password = 'zjp';
$stmt->execute();
$username = 'wxd';
$password = 'wxd';
$stmt->execute();
//第二種繫結引數的方法
$sql = "SELECT * FROM user WHERE username=? AND password=?";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(0, $username);
$stmt->bindParam(1, $password);
$username = 'zjp';
$password = 'zjp';
$stmt->execute();
$username = 'wxd';
$username = 'wxd';
$stmt->execute();

4.9bindValue()繫結值
與bindParam()的區別在於,bindValue()是可以繫結一個特定值得,而bindParam()只能繫結地址,也就是變數。

$sql = "INSERT user(username, password, email) VALUES(?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(0, $uesrname);
$stmt->bindValue(1, $password);
$stmt->bindValue(3, '[email protected]');
$username = 'zjp';
$password = 'zjp';
$stmt->execute();

這樣新增進去的資料,email的值就都為[email protected]了。
4.10bindColumn()方法
簡單的舉一個例子就能看懂了:

$sql = "SELECT username,password,email FROM user";
$stmt = $pdo->prepare($sql);
$stmt->execute();
//獲取結果集中的列數
echo '結果集中的列數一共有:'.$stmt->columnCount().'<hr/>';
$stmt->bindColumn(1, $username); //繫結第一列到$username下
$stmt->bindColumn(2, $password); //繫結第二列到$password下
$stmt->bindColumn(3, $email);    //繫結第三列到$email下
//取出資料,這裡要使用fetch中一個為bindColumn()準備的引數
while($stmt->fetch(PDO::FETCH_BOUND)) {
    echo '使用者名稱:'.$username.'-密碼:'.$password.'-郵箱:'.$email.'<br/>';
}

4.11debugDumpParams()方法
用來列印一條預處理語句

$stmt->execute();
$stmt->debugDumpParams();

5.PDO錯誤處理模式

<?php 
/*
 * PDO::ERRMODE_SLIENT:預設模式,靜默模式
 */
try {
    $pdo = new PDO('mysql:host=localhost;dbname=testpdo', 'root', '');
    $sql = 'SELECT * FROM user12'; //user12不存在
    $pdo->query($sql);
    //這時候並不會輸出任何資訊,只有使用$pdo->errorCode()方法和$pdo->errorInfo()方法來獲取錯誤訊息
}catch(PDOException $e) {
    echo $e->getMessage();
}
?>


<?php 
/*
 * PDO::ERRMODE_WARNING:警告模式
 */
try {
    $pdo = new PDO('mysql:host=localhost;dbname=testpdo', 'root', '');
    //設定錯誤模式
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
    $sql = 'SELECT * FROM user12'; //user12不存在
    $pdo->query($sql);
    //這時候會出現warning的警告訊息,然後使用$pdo->errorCode()方法和$pdo->errorInfo()方法來可以獲取錯誤訊息
}catch(PDOException $e) {
    echo $e->getMessage();
}
?>


<?php 
/*
 * PDO::ERRMODE_EXCEPTION:異常模式
 */
try {
    $pdo = new PDO('mysql:host=localhost;dbname=testpdo', 'root', '');
    //設定錯誤模式
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT * FROM user12'; //user12不存在
    $pdo->query($sql);
    //這時候會出現具體的錯誤資訊,包括錯誤程式碼,錯誤訊息,就不需要使用$pdo->errorCode()方法和$pdo->errorInfo()方法來可以獲取錯誤訊息,這也是我們最常使用的錯誤模式
}catch(PDOException $e) {
    echo $e->getMessage();
}
?>

6.PDO事務處理

例子:
記住要使用INNODB表引擎

<?php 
header('Content-Type:text/html;charset=utf-8');
try {
    $dsn = 'mysql:host=localhost;dbname=testpdo';
    $username = 'root';
    $password = '';
    $options = array(PDO::ATTR_AUTOCOMMIT, 0);//取消自動提交
    $pdo = new PDO($dsn, $username, $password, $options);
    //開啟事務
    $pdo->beginTransaction();
    $sql1 = 'UPDATE account SET money=money-200 WHERE username="zjp"';
    $res1 = $pdo->exec($sql1);
    if($res1==0) {
        throw new PDOException('zjp轉賬失敗');
    }
    $sql2 = 'UPDATE account SET money=money+200 WHERE username="wxd"';
    $res2 = $pdo->exec($sql2);
    if($res2==0) {
        throw new PDOException('wxd接收失敗');
    }
    //提交事務
    $pdo->commit();
}catch(PDOException $e) {
    //回滾事務
    $pdo->rollBack(); 
    echo $e->getMessage();
}
?>

這裡多提一點,也是前幾天面試時候面試官考察我的地方。
在這個事務中,如果我的第一條SQL語句是對的,第二條SQL語句是錯誤的,那麼當我執行第一條SQL語句後,查詢一下值得時候,第一條SQL語句中的zjp的money變沒變,這裡大家可以自己測試一下,其實是變了的。當第二條SQL錯誤的時候,才會回滾。