鍍金池/ 教程/ PHP/ Yii Framework 開發(fā)教程(24) 數(shù)據(jù)庫-DAO 示例
Yii Framework 開發(fā)教程(16) UI 組件 StarRating 示例
Yii Framework 開發(fā)教程(2) Yii Web 應(yīng)用基礎(chǔ)
Yii Framework 開發(fā)教程(19) UI 組件 TreeView 示例
Yii Framework 開發(fā)教程(39) Zii 組件-Slider 示例
Yii Framework 開發(fā)教程(45) Zii 組件-Selectable 示例
Yii Framework 開發(fā)教程(44) Zii 組件-Resizable 示例
Yii Framework 開發(fā)教程(8) 使用 FormModel
Yii Framework 開發(fā)教程(42) Zii 組件-Draggable 示例
Yii Framework 開發(fā)教程(18) UI 組件 TextHighlighter 示例
Yii Framework 開發(fā)教程(32) Zii 組件-GridView 示例
Yii Framework 開發(fā)教程(30) Zii 組件-ListView 示例
Yii Framework 開發(fā)教程(9) UI 組件 Widget 概述
Yii Framework 開發(fā)教程(17) UI 組件 TabView 示例
Yii Framework 開發(fā)教程(24) 數(shù)據(jù)庫-DAO 示例
Yii Framework 開發(fā)教程(25) 數(shù)據(jù)庫-Query Builder 示例
Yii Framework 開發(fā)教程(21) UI 組件 自定義 Captcha 示例
Yii Framework 開發(fā)教程(38) Zii 組件-ProgressBar 示例
Yii Framework 開發(fā)教程(20) UI 組件 Captcha 示例
Yii Framework 開發(fā)教程(14) UI 組件 MaskedTextField 示例
Yii Framework 開發(fā)教程(22) UI 組件 Zii 組件簡介
Yii Framework 開發(fā)教程(31) Zii 組件-DetailView 示例
Yii Framework 開發(fā)教程(33) Zii 組件-Accordion 示例
Yii Framework 開發(fā)教程(36) Zii 組件-DatePicker 示例
Yii Framework 開發(fā)教程(6) CComponent 組件
Yii Framework 開發(fā)教程(37) Zii 組件-Dialog 示例
Yii Framework 開發(fā)教程(26) 數(shù)據(jù)庫-Active Record 示例
Yii Framework 開發(fā)教程(29) Zii組件-Menu 示例
Yii Framework 開發(fā)教程(46) Zii 組件-Sortable 示例
Yii Framework 開發(fā)教程(10) UI 組件 自定義組件
Yii Framework 開發(fā)教程(11) UI 組件 ActiveForm 示例
Yii Framework 開發(fā)教程(43) Zii 組件-Droppable 示例
Yii Framework 開發(fā)教程(27) 數(shù)據(jù)庫-關(guān)聯(lián) Active Record 示例
Yii Framework 開發(fā)教程(47) 主題 Theme 示例
Yii Framework 開發(fā)教程(48) 多國語言示例
Yii Framework 開發(fā)教程(35) Zii 組件-Button 示例
Yii Framework 開發(fā)教程(3) 為應(yīng)用添加日志
Yii Framework 開發(fā)教程(23) 數(shù)據(jù)庫-概述
Yii Framework 開發(fā)教程(12) UI 組件 ClipWidget 示例
Yii Framework 開發(fā)教程(41) Zii 組件-Tabs 示例
Yii Framework 開發(fā)教程(34) Zii 組件-AutoComplete 示例
Yii Framework 開發(fā)教程(40) Zii 組件-SliderInput 示例
Yii Framework 開發(fā)教程(5) URL 管理
Yii Framework 開發(fā)教程(4) Hangman 猜單詞游戲?qū)嵗?/span>
Yii Framework 開發(fā)教程(15) UI 組件 MultiFileUpload 示例
Yii Framework 開發(fā)教程(7) 使用 CHtml 創(chuàng)建 Form
Yii Framework 開發(fā)教程(28) Data Provider 簡介
Yii Framework 開發(fā)教程(1) 第一個應(yīng)用 Hello World
Yii Framework 開發(fā)教程(13) UI 組件 ContentDecorator 示例

Yii Framework 開發(fā)教程(24) 數(shù)據(jù)庫-DAO 示例

Contents

  1. 建立數(shù)據(jù)庫連接
  2. 執(zhí)行 SQL 語句
  3. 獲取查詢結(jié)果
  4. 顯示查詢結(jié)果
  5. 使用事務(wù)
  6. 綁定參數(shù)
  7. 綁定列
  8. 使用表前綴

數(shù)據(jù)訪問對象(DAO) 對訪問存儲在不同數(shù)據(jù)庫管理系統(tǒng)(DBMS)中的數(shù)據(jù)提供了一個通用的 API。 因此,在將底層 DBMS 更換為另一個時,無需修改使用了 DAO 訪問數(shù)據(jù)的代碼。

Yii DAO 基于 PHP Data Objects (PDO) 構(gòu)建。它是一個為眾多流行的DBMS提供統(tǒng)一數(shù)據(jù)訪問的擴展,這些 DBMS 包括 MySQL, PostgreSQL 等等。因此,要使用 Yii DAO,PDO 擴展和特定的 PDO 數(shù)據(jù)庫驅(qū)動(例如 PDO_MYSQL) 必須安裝。

Yii DAO 主要包含如下四個類:

  • CDbConnection: 代表一個數(shù)據(jù)庫連接。
  • CDbCommand: 代表一條通過數(shù)據(jù)庫執(zhí)行的 SQL 語句。
  • CDbDataReader: 代表一個只向前移動的,來自一個查詢結(jié)果集中的行的流。
  • CDbTransaction: 代表一個數(shù)據(jù)庫事務(wù)。

下面,我們介紹 Yii DAO 在不同場景中的應(yīng)用。

1. 建立數(shù)據(jù)庫連接

要建立一個數(shù)據(jù)庫連接,創(chuàng)建一個 CDbConnection 實例并將其激活。 連接到數(shù)據(jù)庫需要一個數(shù)據(jù)源的名字(DSN)以指定連接信息。用戶名和密碼也可能會用到。 當連接到數(shù)據(jù)庫的過程中發(fā)生錯誤時 (例如,錯誤的 DSN 或無效的用戶名/密碼),將會拋出一個異常。


    $connection=new CDbConnection($dsn,$username,$password);
    // 建立連接。你可以使用  try...catch 捕獲可能拋出的異常
    $connection->active=true;
    ......
    $connection->active=false;  // 關(guān)閉連接

DSN 的格式取決于所使用的 PDO 數(shù)據(jù)庫驅(qū)動??傮w來說, DSN 要含有 PDO 驅(qū)動的名字,跟上一個冒號,再跟上驅(qū)動特定的連接語法。可查閱 PDO 文檔 獲取更多信息。 下面是一個常用 DSN 格式的列表。

  • SQLite: sqlite:/path/to/dbfile
  • MySQL: mysql:host=localhost;dbname=testdb
  • PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb
  • SQL Server: mssql:host=localhost;dbname=testdb
  • Oracle: oci:dbname=//localhost:1521/testdb

由于 CDbConnection 繼承自 CApplicationComponent,我們也可以將其作為一個 應(yīng)用組件 使用。要這樣做的話, 請在 應(yīng)用配置 中配置一個 db (或其他名字)應(yīng)用組件如下:

本例使用 MySQL chinook 數(shù)據(jù)庫,修改 protected/config/main.php


    'components'=>array(
        'db'=>array(
                'class'=>'CDbConnection',
                'connectionString'=>'mysql:host=localhost;dbname=chinook',
                'username'=>'root',
                'password'=>'password',
                'emulatePrepare'=>true,  // needed by some MySQL installations
                ),
            ),

然后我們就可以通過 Yii::app()->db 訪問數(shù)據(jù)庫連接了。它已經(jīng)被自動激活了,除非我們特意配置了 CDbConnection::autoConnect 為 false。通過這種方式,這個單獨的DB連接就可以在我們代碼中的很多地方共享。

2. 執(zhí)行 SQL 語句

數(shù)據(jù)庫連接建立后,SQL 語句就可以通過使用 CDbCommand 執(zhí)行了。你可以通過使用指定的 SQL 語句作為參數(shù)調(diào)用 CDbConnection::createCommand() 創(chuàng)建一個 CDbCommand 實例。

為簡單起見,我們使用 Chinook 數(shù)據(jù)庫中的 Employee 表,修改 DataModel


    class DataModel
    {
        public $employeeId;
        public $firstName;
        public $lastName;
        public $title;
        public $address;
        public $email;
    }

注:創(chuàng)建 DataModel 這一步可以選。

修改 SiteController 的 indexAction 方法:


    public function actionIndex()
        {

            $model = array();
            $sql='SELECT * FROM Employee';
            // 假設(shè)你已經(jīng)建立了一個 "db" 連接
            $connection=Yii::app()->db;
            // 如果沒有,你可能需要顯式建立一個連接:
            // $connection=new CDbConnection($dsn,$username,$password);
            $command=$connection->createCommand($sql);
            // 如果需要,此 SQL 語句可通過如下方式修改:
            // $command->text=$newSQL;

            $dataReader=$command->query();

            // each $row is an array representing a row of data
            foreach($dataReader as $row)
            {
                $employee = new DataModel();
                $employee->employeeId=$row['EmployeeId'];
                $employee->firstName=$row['FirstName'];
                $employee->lastName=$row['LastName'];
                $employee->title=$row['Title'];
                $employee->address=$row['Address'];
                $employee->email=$row['Email'];

                $model[]=$employee;
            }

            $this->render('index', array(
                'model' => $model,

                ));
        }

一條 SQL 語句會通過 CDbCommand 以如下兩種方式被執(zhí)行:

  • execute(): 執(zhí)行一個無查詢 (non-query)SQL 語句, 例如 INSERT, UPDATE 和 DELETE 。如果成功,它將返回此執(zhí)行所影響的行數(shù)。
  • query(): 執(zhí)行一條會返回若干行數(shù)據(jù)的 SQL 語句,例如 SELECT。 如果成功,它將返回一個 CDbDataReader 實例,通過此實例可以遍歷數(shù)據(jù)的結(jié)果行。為簡便起見, (Yii)還實現(xiàn)了一系列 queryXXX() 方法以直接返回查詢結(jié)果。

執(zhí)行 SQL 語句時如果發(fā)生錯誤,將會拋出一個異常。

$rowCount=$command->execute();   // 執(zhí)行無查詢 SQL
$dataReader=$command->query();   // 執(zhí)行一個 SQL 查詢
$rows=$command->queryAll();      // 查詢并返回結(jié)果中的所有行
$row=$command->queryRow();       // 查詢并返回結(jié)果中的第一行
$column=$command->queryColumn(); // 查詢并返回結(jié)果中的第一列
$value=$command->queryScalar();  // 查詢并返回結(jié)果中第一行的第一個字段

3. 獲取查詢結(jié)果

CDbCommand::query() 生成 CDbDataReader 實例之后,你可以通過重復(fù)調(diào)用 CDbDataReader::read() 獲取結(jié)果中的行。你也可以在 PHP 的 foreach 語言結(jié)構(gòu)中使用 CDbDataReader 一行行檢索數(shù)據(jù)。

$dataReader=$command->query();
// 重復(fù)調(diào)用 read() 直到它返回 false
while(($row=$dataReader->read())!==false) { ... }
// 使用 foreach 遍歷數(shù)據(jù)中的每一行
foreach($dataReader as $row) { ... }
// 一次性提取所有行到一個數(shù)組
$rows=$dataReader->readAll();

4. 顯示查詢結(jié)果

為簡單起見,本例使用 echo 語句顯示 Employee 的記錄,后面可以使用 GridView 或是 ListView 來顯示數(shù)據(jù)庫的表格。

修改 protected/views/site/index.php


    <?php foreach($model as $employee)
    {

        echo 'EmployeeId:' . $employee->employeeId . '<br />';
        echo 'First Name:' . $employee->firstName . '<br />';
        echo 'Last Name:' . $employee->lastName . '<br />';
        echo 'Title:' . $employee->title . '<br />';
        echo 'Address:' . $employee->address . '<br />';
        echo 'Email:' . $employee->email . '<br />';
        echo '---------------------- <br />';
    }

     ?>

http://wiki.jikexueyuan.com/project/yii-development-tutorial/images/24.1.jpg" alt="picture24.1" />

本例下載 此外 Dao 支持事務(wù)處理,綁定參數(shù)等。

5. 使用事務(wù)

當一個應(yīng)用要執(zhí)行幾條查詢,每條查詢要從數(shù)據(jù)庫中讀取并/或向數(shù)據(jù)庫中寫入信息時, 保證數(shù)據(jù)庫沒有留下幾條查詢而只執(zhí)行了另外幾條查詢是非常重要的。 事務(wù),在 Yii 中表現(xiàn)為 CDbTransaction 實例,可能會在下面的情況中啟動:

  • 開始事務(wù).
  • 一個個執(zhí)行查詢。任何對數(shù)據(jù)庫的更新對外界不可見。
  • 提交事務(wù)。如果事務(wù)成功,更新變?yōu)榭梢姟?/li>
  • 如果查詢中的一個失敗,整個事務(wù)回滾。

上述工作流可以通過如下代碼實現(xiàn):


    $transaction=$connection->beginTransaction();
    try
    {
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //.... other SQL executions
    $transaction->commit();
    }
    catch(Exception $e) // 如果有一條查詢失敗,則會拋出異常
    {
    $transaction->rollBack();
    }

6. 綁定參數(shù)

要避免 SQL 注入攻擊 并提高重復(fù)執(zhí)行的 SQL 語句的效率, 你可以 “準備(prepare)”一條含有可選參數(shù)占位符的 SQL 語句,在參數(shù)綁定時,這些占位符將被替換為實際的參數(shù)。

參數(shù)占位符可以是命名的 (表現(xiàn)為一個唯一的標記) 或未命名的 (表現(xiàn)為一個問號)。調(diào)用 CDbCommand::bindParam()CDbCommand::bindValue() 以使用實際參數(shù)替換這些占位符。 這些參數(shù)不需要使用引號引起來:底層的數(shù)據(jù)庫驅(qū)動會為你搞定這個。 參數(shù)綁定必須在 SQL 語句執(zhí)行之前完成。


    // 一條帶有兩個占位符 ":username" 和 ":email"的 SQL
    $sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";
    $command=$connection->createCommand($sql);
    // 用實際的用戶名替換占位符 ":username"
    $command->bindParam(":username",$username,PDO::PARAM_STR);
    // 用實際的 Email 替換占位符 ":email"
    $command->bindParam(":email",$email,PDO::PARAM_STR);
    $command->execute();
    // 使用新的參數(shù)集插入另一行
    $command->bindParam(":username",$username2,PDO::PARAM_STR);
    $command->bindParam(":email",$email2,PDO::PARAM_STR);
    $command->execute();

方法 bindParam()bindValue() 非常相似。唯一的區(qū)別就是前者使用一個 PHP 變量綁定參數(shù), 而后者使用一個值。對于那些內(nèi)存中的大數(shù)據(jù)塊參數(shù),處于性能的考慮,應(yīng)優(yōu)先使用前者。

關(guān)于綁定參數(shù)的更多信息,請參考 相關(guān)的PHP文檔。

7. 綁定列

當獲取查詢結(jié)果時,你也可以使用 PHP 變量綁定列。 這樣在每次獲取查詢結(jié)果中的一行時就會自動使用最新的值填充。


    $sql="SELECT username, email FROM tbl_user";
    $dataReader=$connection->createCommand($sql)->query();
    // 使用 $username 變量綁定第一列 (username)
    $dataReader->bindColumn(1,$username);
    // 使用 $email 變量綁定第二列 (email)
    $dataReader->bindColumn(2,$email);
    while($dataReader->read()!==false)
    {
    // $username 和 $email 含有當前行中的 username 和 email
    }

8. 使用表前綴

從版本 1.1.0 起, Yii 提供了集成了對使用表前綴的支持。 表前綴是指在當前連接的數(shù)據(jù)庫中的數(shù)據(jù)表的名字前面添加的一個字符串。 它常用于共享的服務(wù)器環(huán)境,這種環(huán)境中多個應(yīng)用可能會共享同一個數(shù)據(jù)庫,要使用不同的表前綴以相互區(qū)分。 例如,一個應(yīng)用可以使用 tbl 作為表前綴而另一個可以使用 yii。

要使用表前綴,配置 CDbConnection::tablePrefix 屬性為所希望的表前綴。 然后,在 SQL 語句中使用{{TableName}} 代表表的名字,其中的 TableName 是指不帶前綴的表名。 例如,如果數(shù)據(jù)庫含有一個名為 bluser 的表,而 tbl 被配置為表前綴,那我們就可以使用如下代碼執(zhí)行用戶相關(guān)的查詢:


    $sql='SELECT * FROM {{user}}';
    $users=$connection->createCommand($sql)->queryAll();