MySQL數據庫PDO教程

翻譯爛到家了,看不順眼輕噴。。。php

1.爲何要使用PDO?


        mysql_*函數已通過時,至關一段時間以來,mysql_*函數在其餘SQL數據庫編程接口方面已經有所差異;它不支持預處理,存儲過程,事務等一些現代數據庫設計思想,SQL語句字符串轉義函數mysql_real_escape_string() 和 拼接SQL語句的編程方法 已通過時而且很容易出錯。最近一段時間裏,它缺少開發者的關注,缺乏維護將可能致使一些安全問題不能被即時修復,或者在適配新版本的MySQL的時候不能正常工做,這成爲mysql_*函數面臨的的另外一個問題。PHP社區最近也對mysql_*函數給出不建議使用的建議,也有可能在將來的版本中最終被棄用(不過不用過於擔憂,這可能還須要很長一段時間)。html

        PDO擁有更好的編程接口,你可使用它寫出更加簡潔,高效,安全的代碼。PDO還爲不一樣的SQL數據庫提供了不一樣的驅動,方便你使用新的數據庫而不用再學習不一樣的編程接口。與拼接SQL語句構造查詢語句不一樣,綁定參數能夠簡潔方便的構造出更加安全的查詢語句,使用綁定參數的方法在 屢次類似語句查詢(僅僅某個參數不一樣)中也能夠提升很多性能。PDO在錯誤處理方面也提供了多種方法。mysql_*函數缺少一致的處理,與PDO的異常模式相比,或者說沒有處理異常,使用PDO,你能夠獲得一致的錯誤處理,這將節省您大量的時間來跟蹤問題。mysql

        在當前的PHP版本中,PDO模塊是默認安裝啓用的,可是在使用PDO前你還須要安裝另外兩個軟件包,一個是pdo_mysql數據庫驅動程序,另一個是相似php-mysql的mysql驅動程序。sql

2.鏈接MySQL


2.1. 之前的方式:

 
  1. $link = mysql_connect('localhost', 'user', 'pass');
  2. mysql_select_db('testdb', $link);
  3. mysql_set_charset('UTF-8', $link);

2.2. 新的方式:

* 建立一個PDO對象,參數包括 DSN, username, password 和 一個驅動選項的數組(可忽略)。
* DSN其實就是一個告訴PDO該使用哪種數據庫驅動 和 一些鏈接信息的字符串,瞭解更多 PDO MYSQL DSN .數據庫

 
  1. $db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

注意:確保DSN中設置了字符編碼信息,不然將可能返回字符編碼設置錯誤的信息,出於安全考慮,DSN最好包括字符編碼信息設置。編程

         你也能夠在第四個參數數組裏填寫一些驅動選項,建議將 PDO異常模式(下文講解) 和關閉預處理模擬(默認打開的,僅對於舊版本MySQL有用)兩個參數加入到第四個參數數組中。數組

 
  1. $db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password', array(PDO::ATTR_EMULATE_PREPARES => false,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

你也能夠在建立PDO對象後再經過setAttribute方法設置相應選項。安全

 
  1. $db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');
  2. $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  3. $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

3.錯誤處理


3.1. mysql_*函數的錯誤處理

 
  1. //connected to mysql
  2. $result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die()是個不錯的錯誤處理方法,可是會所以結束頁面,將錯誤信息呈現到用戶面前,這多是咱們不想看到的結果。
PDO有三種錯誤處理模式服務器

  1. PDO::ERRMODE_SILENT # 和 mysql_*函數相似,檢查代碼並查看 $db->errorInfo(); 獲取詳細信息。
  2. PDO::ERRMODE_WARNING # 拋出PHP警告。
  3. PDO::ERRMODE_EXCEPTION #拋出 PDOException 異常,在我認爲,這是咱們應該使用的模式, 這和 die(mysql_error()); 相似,可是它能夠捕獲並拋出具體異常信息。

3.2. code:

 
  1. try {
  2. //connect as appropriate as above
  3. $db->query('hi'); //invalid query!
  4. } catch(PDOException $ex) {
  5. echo "An Error occured!"; //user friendly message
  6. some_logging_function($ex->getMessage());
  7. }

注意:你能夠不用當即執行並捕獲異常,你能夠在任何合適的時候隨時捕獲。app

 
  1. function getData($db) {
  2. $stmt = $db->query("SELECT * FROM table");
  3. return $stmt->fetchAll(PDO::FETCH_ASSOC);
  4. }
  5.  
  6. //then much later
  7. try {
  8. getData($db);
  9. } catch(PDOException $ex) {
  10. //handle me.
  11. }

若是你不想使用try/catch來處理異常,就像使用OR die()那樣處理,在production模式下關閉display_errors選項便可。

4.簡單的查詢語句(SELECT


4.1. mysql_*代碼:

 
  1. $result = mysql_query('SELECT * from table') or die(mysql_error());
  2.  
  3. $num_rows = mysql_num_rows($result);
  4.  
  5. while($row = mysql_fetch_assoc($result)) {
  6. echo $row['field1'].' '.$row['field2']; //etc...
  7. }

4.2. PDO代碼:

 
  1. foreach($db->query('SELECT * FROM table') as $row) {
  2. echo $row['field1'].' '.$row['field2']; //etc...
  3. }

query() 方法返回了一個 PDOStatement 對象,你能夠經過以下方法獲取結果:

 
  1. $stmt = $db->query('SELECT * FROM table');
  2.  
  3. while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  4. echo $row['field1'].' '.$row['field2']; //etc...
  5. }

或者

 
  1. $stmt = $db->query('SELECT * FROM table');
  2. $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
  3. //use $results

4.3. # Fetch Modes

注意 fetch() 和 fetchAll() 代碼中的PDO::FETCH_ASSOC ,它高速 PDO 以關聯數組的形式返回 鍵,值;其餘好比PDO::FETCH_NUM模式,則返回數值鍵值的數組,默認模式是 PDO::FETCH_BOTH 則返回前面二者的集合,既有數值鍵值的數組,又有關聯數組。PDO也能夠獲取數據返回對象PDO::FETCH_OBJPDO::FETCH_CLASSPDO::FETCH_BOUND,bindColumn方法等更多內容,請閱讀: PDOStatement Fetch documentation

4.4. # 獲取數據行數(Getting Row Count)

代替 mysql_num_rows 方法,你可使用 PDOStatement對象的rowCount();方法。

 
  1. $stmt = $db->query('SELECT * FROM table');
  2. $row_count = $stmt->rowCount();
  3. echo $row_count.' rows selected';

注意:官方文檔稱此函數僅適用於返回 `UPDATE`, `INSERT`, `DELETE`操做的`affected rows`,而 `SELECT`操做,僅對於`PDO_MYSQL` 驅動,此函數一樣適用(謹記),在操做其餘數據庫的時候尤爲注意。

4.5. # 獲取最後操做ID(Getting the Last Insert Id)

mysql_*代碼:

 
  1. $result = mysql_query("INSERT INTO table(firstname, lastname) VALUES('John', 'Doe')") or die("Insert Failed ".mysql_error());
  2. $insert_id = mysql_insert_id();

4.6. PDO代碼:

 
  1. $result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
  2. $insertId = $db->lastInsertId();

5.執行 INSERTUPDATEDELETE 操做


5.1. mysql_*代碼:

 
  1. $results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
  2. $affected_rows = mysql_affected_rows($result);
  3. echo $affected_rows.' were affected';

5.2. PDO代碼:

 
  1. $affected_rows = $db->exec("UPDATE table SET field='value'");
  2. echo $affected_rows.' were affected'

DELETE , INSERT 操做一樣適用。

6.運行帶有查詢參數的語句(Running Statements With Parameters)


        對於 不攜帶任何參數的查詢語句,咱們可使用 query方法處理SELECT操做,使用exec方法處理 INSERTUPDATEINSERT操做,而對於攜帶查詢參數的語句,你應該使用綁定參數的方法來安全的處理這些操做。

6.1. mysql_*代碼:

 
  1. $results = mysql_query(sprintf("SELECT * FROM table WHERE id='%s' AND name='%s'",
  2. mysql_real_escape_string($id), mysql_real_escape_string($name))) or die(mysql_error());
  3. $rows = array();
  4. while($row = mysql_fetch_assoc($results)){
  5. $rows[] = $row;
  6. }

6.2. PDO代碼:

 
  1. $stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");
  2. $stmt->execute(array($id, $name));
  3. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        prepare方法將查詢語句發送到服務器,以「?」做爲參數佔位符進行編譯,execute方法將查詢參數發送到服務器,運行以前編譯好的查詢語句。由於 查詢語句 和 查詢參數 是分開發送的,因此在參數裏的SQL語句是不可能被執行的,因此不會發生 SQL注入,這是一種比鏈接字符串構造SQL語句更加安全的解決方法。

注意: 當你使用**綁定參數**的時候,不要對"?"佔位符使用引號(SQL語句原來是對參數使用引號的),由於參數類型是在execute方法的時候肯定的,因此在prepare的時候沒必要對佔位符使用引號。

        還有一些綁定參數的方法,bindValue方法能夠分別綁定每一個參數來代替execute方法的數組方式,同時還分別設置每一個參數的類型。

 
  1. $stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");
  2. $stmt->bindValue(1, $id, PDO::PARAM_INT);
  3. $stmt->bindValue(2, $name, PDO::PARAM_STR);
  4. $stmt->execute();
  5. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

6.3. #命名佔位符

若是你有許多參數須要綁定,不要使用問號佔位符,以防混淆出錯,你可使用命名佔位符代替問號佔位符

 
  1. $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
  2. $stmt->bindValue(':id', $id, PDO::PARAM_INT);
  3. $stmt->bindValue(':name', $name, PDO::PARAM_STR);
  4. $stmt->execute();
  5. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

你也可使用execute方法,以數組的方式綁定參數:

 
  1. $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
  2. $stmt->execute(array(':name' => $name, ':id' => $id));
  3. $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

6.4. #INSERT, DELETE, UPDATE 預處理查詢

INSERTDELETEUPDATE 預處理語句的使用和SELECT相似,咱們舉幾個例子:

 
  1. $stmt = $db->prepare("INSERT INTO table(field1,field2,field3,field4,field5) VALUES(:field1,:field2,:field3,:field4,:field5)");
  2. $stmt->execute(array(':field1' => $field1, ':field2' => $field2, ':field3' => $field3, ':field4' => $field4, ':field5' => $field5));
  3. $affected_rows = $stmt->rowCount();

 
  1. $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
  2. $stmt->bindValue(':id', $id, PDO::PARAM_STR);
  3. $stmt->execute();
  4. $affected_rows = $stmt->rowCount();

 
  1. $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
  2. $stmt->execute(array($name, $id));
  3. $affected_rows = $stmt->rowCount();

6.5. #在預處理中使用SQL函數

=>無效方法:

 
  1. //THIS WILL NOT WORK!
  2. $time = 'NOW()';
  3. $name = 'BOB';
  4. $stmt = $db->prepare("INSERT INTO table(`time`, `name`) VALUES(?, ?)");
  5. $stmt->execute(array($time, $name));

=>正確方法

 
  1. $name = 'BOB';
  2. $stmt = $db->prepare("INSERT INTO table(`time`, `name`) VALUES(NOW(), ?)");
  3. $stmt->execute(array($name));

=>你也能夠在SQL函數裏綁定參數:

 
  1. $name = 'BOB';
  2. $password = 'badpass';
  3. $stmt = $db->prepare("INSERT INTO table(`hexvalue`, `password`) VALUES(HEX(?), PASSWORD(?))");
  4. $stmt->execute(array($name, $password));

=>可是不能做爲LIKE的參數:

 
  1. //THIS DOES NOT WORK
  2. $stmt = $db->prepare("SELECT field FROM table WHERE field LIKE %?%");
  3. $stmt->bindParam(1, $search, PDO::PARAM_STR);
  4. $stmt->execute();

=>正確使用LIKE並綁定參數的方法:

 
  1. $stmt = $db->prepare("SELECT field FROM table WHERE field LIKE ?");
  2. $stmt->bindValue(1, "%$search%", PDO::PARAM_STR);
  3. $stmt->execute();

注意:這裏使用的是bindValue而不是bindParam,不然會發生PDOException或致命錯誤。

6.6. #使用循環運行預處理語句

預處理語句能夠一次設置,屢次調用,由於僅在第一次傳入的時候編譯,所以在後來的屢次調用中提升了很多效率。
         典型的應用就是bindParambindParambindValue的不一樣之處在於,它不是綁定了參數的值,而是綁定參數變量自己,所以,若是參數變量變化了,那麼在execute處理的時候,查詢也將相應變化。

 
  1. $values = array('bob', 'alice', 'lisa', 'john');
  2. $name = '';
  3. $stmt = $db->prepare("INSERT INTO table(`name`) VALUES(:name)");
  4. $stmt->bindParam(':name', $name, PDO::PARAM_STR);
  5. foreach($values as $name) {
  6. $stmt->execute();
  7. }

7.PDO中的事務(Transactions)


注意:調用`beginTransaction()`方法即自動關閉了`自動提交`。

 
  1. try {
  2. $db->beginTransaction();
  3.  
  4. $db->exec("SOME QUERY");
  5.  
  6. $stmt = $db->prepare("SOME OTHER QUERY?");
  7. $stmt->execute(array($value));
  8.  
  9. $stmt = $db->prepare("YET ANOTHER QUERY??");
  10. $stmt->execute(array($value2, $value3));
  11.  
  12. $db->commit();
  13. } catch(PDOException $ex) {
  14. //Something went wrong rollback!
  15. $db->rollBack();
  16. echo $ex->getMessage();
  17. }

原文連接:PDO Tutorial for MySQL Developers
參考連接:PDO Documentation
延伸閱讀:Validation and SQL Injection

 

http://rmingwang.com/pdo-tutorial-for-mysql-developers.html

相關文章
相關標籤/搜索