鍍金池/ 教程/ PHP/ 數(shù)據(jù)庫(kù)訪問(wèn) (DAO)
查詢構(gòu)建器
HTTP 緩存
單元測(cè)試
資源
數(shù)據(jù)庫(kù)遷移
Fixtures
收集列表輸入
認(rèn)證
助手類(lèi)
緩存
數(shù)據(jù)緩存
最佳安全實(shí)踐
響應(yīng)格式
使用 Gii 生成代碼
服務(wù)定位器
性能優(yōu)化
資源
多模型的復(fù)合表單
控制器
Html 幫助類(lèi)
運(yùn)行機(jī)制概述
快速入門(mén)
屬性(Property)
使用表單
配置測(cè)試環(huán)境
數(shù)據(jù)提供者
使用數(shù)據(jù)庫(kù)
授權(quán)
輸入驗(yàn)證
類(lèi)自動(dòng)加載(Autoloading)
版本
響應(yīng)
Sessions 和 Cookies
數(shù)組助手類(lèi)
創(chuàng)建你自己的應(yīng)用程序結(jié)構(gòu)
文件上傳
路由
收發(fā)郵件
模型
小部件
更上一層樓
頁(yè)面緩存
請(qǐng)求
片段緩存
排序
處理密碼
數(shù)據(jù)小部件
模塊
事件
控制器
從 Yii 1.1 升級(jí)
應(yīng)用組件
驗(yàn)收測(cè)試
入口腳本
總覽
Url 幫助類(lèi)
行為
速率限制
控制臺(tái)命令
依賴注入容器
視圖
功能測(cè)試
錯(cuò)誤處理
過(guò)濾器
主題
應(yīng)用主體
引入第三方代碼
共享托管環(huán)境
測(cè)試
擴(kuò)展
路由
使用模板引擎
核心驗(yàn)證器(Core Validators)
分頁(yè)
數(shù)據(jù)庫(kù)訪問(wèn) (DAO)
配置
創(chuàng)建表單
日志
安裝 Yii
客戶端腳本使用
組件(Component)
說(shuō)聲 Hello
運(yùn)行應(yīng)用
數(shù)據(jù)格式器
認(rèn)證
錯(cuò)誤處理
別名(Aliases)
Active Record
啟動(dòng)引導(dǎo)(Bootstrapping)
國(guó)際化

數(shù)據(jù)庫(kù)訪問(wèn) (DAO)

Yii 包含了一個(gè)建立在 PHP PDO 之上的數(shù)據(jù)訪問(wèn)層 (DAO). DAO為不同的數(shù)據(jù)庫(kù)提供了一套統(tǒng)一的API. 其中ActiveRecord 提供了數(shù)據(jù)庫(kù)與模型(MVC 中的 M,Model) 的交互,QueryBuilder 用于創(chuàng)建動(dòng)態(tài)的查詢語(yǔ)句. DAO提供了簡(jiǎn)單高效的SQL查詢,可以用在與數(shù)據(jù)庫(kù)交互的各個(gè)地方.

Yii 默認(rèn)支持以下數(shù)據(jù)庫(kù) (DBMS):

配置

開(kāi)始使用數(shù)據(jù)庫(kù)首先需要配置數(shù)據(jù)庫(kù)連接組件,通過(guò)添加 db 組件到應(yīng)用配置實(shí)現(xiàn)("基礎(chǔ)的" Web 應(yīng)用是 config/web.php),DSN( Data Source Name )是數(shù)據(jù)源名稱,用于指定數(shù)據(jù)庫(kù)信息.如下所示:

return [
    // ...
    'components' => [
        // ...
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB
            //'dsn' => 'sqlite:/path/to/database/file', // SQLite
            //'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL
            //'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID
            //'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver
            //'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver
            //'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver
            //'dsn' => 'oci:dbname=//localhost:1521/mydatabase', // Oracle
            'username' => 'root', //數(shù)據(jù)庫(kù)用戶名
            'password' => '', //數(shù)據(jù)庫(kù)密碼
            'charset' => 'utf8',
        ],
    ],
    // ...
];

請(qǐng)參考PHP manual獲取更多有關(guān) DSN 格式信息。配置連接組件后可以使用以下語(yǔ)法訪問(wèn):

<<<<<<< HEAD $connection = \Yii::$app->db;

請(qǐng)參考[[yii\db\Connection]]獲取可配置的屬性列表。

$connection = \Yii::$app->db;

請(qǐng)參考[[yii\db\Connection]]獲取可配置的屬性列表。

yiichina/master 如果你想通過(guò)ODBC連接數(shù)據(jù)庫(kù),則需要配置[[yii\db\Connection::driverName]] 屬性,例如:

'db' => [
    'class' => 'yii\db\Connection',
    'driverName' => 'mysql',
    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',
    'username' => 'root',
    'password' => '',
],

注意:如果需要同時(shí)使用多個(gè)數(shù)據(jù)庫(kù)可以定義 多個(gè) 連接組件:

return [
    // ...
    'components' => [
        // ...
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=mydatabase', 
            'username' => 'root',
            'password' => '',
            'charset' => 'utf8',
        ],
        'secondDb' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'sqlite:/path/to/database/file', 
        ],
    ],
    // ...
];

在代碼中通過(guò)以下方式使用:

$primaryConnection = \Yii::$app->db;
$secondaryConnection = \Yii::$app->secondDb;

如果不想定義數(shù)據(jù)庫(kù)連接為全局應(yīng)用組件,可以在代碼中直接初始化使用:

$connection = new \yii\db\Connection([
    'dsn' => $dsn,
     'username' => $username,
     'password' => $password,
]);
$connection->open();

小提示:如果在創(chuàng)建了連接后需要執(zhí)行額外的 SQL 查詢,可以添加以下代碼到應(yīng)用配置文件:

return [
    // ...
    'components' => [
        // ...
        'db' => [
            'class' => 'yii\db\Connection',
            // ...
            'on afterOpen' => function($event) {
                $event->sender->createCommand("SET time_zone = 'UTC'")->execute();
            }
        ],
    ],
    // ...
];

SQL 基礎(chǔ)查詢

一旦有了連接實(shí)例就可以通過(guò)[[yii\db\Command]]執(zhí)行 SQL 查詢。

SELECT 查詢

查詢返回多行:

$command = $connection->createCommand('SELECT * FROM post');
$posts = $command->queryAll();

返回單行: <<<<<<< HEAD

yiichina/master

$command = $connection->createCommand('SELECT * FROM post WHERE id=1');
$post = $command->queryOne();

查詢多行單值: <<<<<<< HEAD

yiichina/master

$command = $connection->createCommand('SELECT title FROM post');
$titles = $command->queryColumn();

查詢標(biāo)量值/計(jì)算值:

$command = $connection->createCommand('SELECT COUNT(*) FROM post');
$postCount = $command->queryScalar();

UPDATE, INSERT, DELETE 更新、插入和刪除等

如果執(zhí)行 SQL 不返回任何數(shù)據(jù)可使用命令中的 execute 方法:

$command = $connection->createCommand('UPDATE post SET status=1 WHERE id=1');
$command->execute();

你可以使用insert,update,delete 方法,這些方法會(huì)根據(jù)參數(shù)生成合適的SQL并執(zhí)行.

// INSERT
$connection->createCommand()->insert('user', [
    'name' => 'Sam',
    'age' => 30,
])->execute();

// INSERT 一次插入多行
$connection->createCommand()->batchInsert('user', ['name', 'age'], [
    ['Tom', 30],
    ['Jane', 20],
    ['Linda', 25],
])->execute();

// UPDATE
$connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();

// DELETE
$connection->createCommand()->delete('user', 'status = 0')->execute();

引用的表名和列名

大多數(shù)時(shí)間都使用以下語(yǔ)法來(lái)安全地引用表名和列名:

$sql = "SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();

以上代碼[[$column]] 會(huì)轉(zhuǎn)變?yōu)橐们‘?dāng)?shù)牧忻?code>{{table}} 就轉(zhuǎn)變?yōu)橐们‘?dāng)?shù)谋砻?表名有個(gè)特殊的變量 {{%Y}} ,如果設(shè)置了表前綴使用該變體可以自動(dòng)在表名前添加前綴:

$sql = "SELECT COUNT([[$column]]) FROM {{%$table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();

如果在配置文件如下設(shè)置了表前綴,以上代碼將在 tbl_table 這個(gè)表查詢結(jié)果:

return [
    // ...
    'components' => [
        // ...
        'db' => [
            // ...
            'tablePrefix' => 'tbl_',
        ],
    ],
];

手工引用表名和列名的另一個(gè)選擇是使用[[yii\db\Connection::quoteTableName()]] 和 [[yii\db\Connection::quoteColumnName()]]:

$column = $connection->quoteColumnName($column);
$table = $connection->quoteTableName($table);
$sql = "SELECT COUNT($column) FROM $table";
$rowCount = $connection->createCommand($sql)->queryScalar();

預(yù)處理語(yǔ)句

為安全傳遞查詢參數(shù)可以使用預(yù)處理語(yǔ)句,首先應(yīng)當(dāng)使用:placeholder占位,再將變量綁定到對(duì)應(yīng)占位符:

$command = $connection->createCommand('SELECT * FROM post WHERE id=:id');
$command->bindValue(':id', $_GET['id']);
$post = $command->query();

另一種用法是準(zhǔn)備一次預(yù)處理語(yǔ)句而執(zhí)行多次查詢:

$command = $connection->createCommand('DELETE FROM post WHERE id=:id');
$command->bindParam(':id', $id);

$id = 1;
$command->execute();

$id = 2;
$command->execute();

提示,在執(zhí)行前綁定變量,然后在每個(gè)執(zhí)行中改變變量的值(一般用在循環(huán)中)比較高效.

事務(wù)

當(dāng)你需要順序執(zhí)行多個(gè)相關(guān)的的query時(shí),你可以把他們封裝到一個(gè)事務(wù)中去保護(hù)數(shù)據(jù)一致性.Yii提供了一個(gè)簡(jiǎn)單的接口來(lái)實(shí)現(xiàn)事務(wù)操作.如下執(zhí)行 SQL 事務(wù)查詢語(yǔ)句:

$transaction = $connection->beginTransaction();
try {
    $connection->createCommand($sql1)->execute();
     $connection->createCommand($sql2)->execute();
    // ... 執(zhí)行其他 SQL 語(yǔ)句 ...
    $transaction->commit();
} catch(Exception $e) {
    $transaction->rollBack();
}

我們通過(guò)[[yii\db\Connection::beginTransaction()|beginTransaction()]]開(kāi)始一個(gè)事務(wù),通過(guò)try catch 捕獲異常.當(dāng)執(zhí)行成功,通過(guò)[[yii\db\Transaction::commit()|commit()]]提交事務(wù)并結(jié)束,當(dāng)發(fā)生異常失敗通過(guò)[[yii\db\Transaction::rollBack()|rollBack()]]進(jìn)行事務(wù)回滾.

如需要也可以嵌套多個(gè)事務(wù):

// 外部事務(wù)
$transaction1 = $connection->beginTransaction();
try {
    $connection->createCommand($sql1)->execute();

    // 內(nèi)部事務(wù)
    $transaction2 = $connection->beginTransaction();
    try {
        $connection->createCommand($sql2)->execute();
        $transaction2->commit();
    } catch (Exception $e) {
        $transaction2->rollBack();
    }

    $transaction1->commit();
} catch (Exception $e) {
    $transaction1->rollBack();
}

注意你使用的數(shù)據(jù)庫(kù)必須支持Savepoints才能正確地執(zhí)行,以上代碼在所有關(guān)系數(shù)據(jù)中都可以執(zhí)行,但是只有支持Savepoints才能保證安全性。

Yii 也支持為事務(wù)設(shè)置隔離級(jí)別isolation levels,當(dāng)執(zhí)行事務(wù)時(shí)會(huì)使用數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別,你也可以為事物指定隔離級(jí)別.Yii 提供了以下常量作為常用的隔離級(jí)別

  • [[\yii\db\Transaction::READ_UNCOMMITTED]] - 允許讀取改變了的還未提交的數(shù)據(jù),可能導(dǎo)致臟讀、不可重復(fù)讀和幻讀
  • [[\yii\db\Transaction::READ_COMMITTED]] - 允許并發(fā)事務(wù)提交之后讀取,可以避免臟讀,可能導(dǎo)致重復(fù)讀和幻讀。
  • [[\yii\db\Transaction::REPEATABLE_READ]] - 對(duì)相同字段的多次讀取結(jié)果一致,可導(dǎo)致幻讀。
  • [[\yii\db\Transaction::SERIALIZABLE]] - 完全服從ACID的原則,確保不發(fā)生臟讀、不可重復(fù)讀和幻讀。

你可以使用以上常量或者使用一個(gè)string字符串命令,在對(duì)應(yīng)數(shù)據(jù)庫(kù)中執(zhí)行該命令用以設(shè)置隔離級(jí)別,比如對(duì)于postgres有效的命令為SERIALIZABLE READ ONLY DEFERRABLE.

注意:某些數(shù)據(jù)庫(kù)只能針對(duì)連接來(lái)設(shè)置事務(wù)隔離級(jí)別,所以你必須要為連接明確制定隔離級(jí)別.目前受影響的數(shù)據(jù)庫(kù):MSSQL SQLite

注意:SQLite 只支持兩種事務(wù)隔離級(jí)別,所以你只能設(shè)置READ UNCOMMITTEDSERIALIZABLE.使用其他隔離級(jí)別會(huì)拋出異常.

注意:PostgreSQL 不允許在事務(wù)開(kāi)始前設(shè)置隔離級(jí)別,所以你不能在事務(wù)開(kāi)始時(shí)指定隔離級(jí)別.你可以在事務(wù)開(kāi)始之后調(diào)用[[yii\db\Transaction::setIsolationLevel()]] 來(lái)設(shè)置.

關(guān)于隔離級(jí)別[isolation levels]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels

數(shù)據(jù)庫(kù)復(fù)制和讀寫(xiě)分離

很多數(shù)據(jù)庫(kù)支持?jǐn)?shù)據(jù)庫(kù)復(fù)制 database replication來(lái)提高可用性和響應(yīng)速度. 在數(shù)據(jù)庫(kù)復(fù)制中,數(shù)據(jù)總是從主服務(wù)器從服務(wù)器. 所有的插入和更新等寫(xiě)操作在主服務(wù)器執(zhí)行,而讀操作在從服務(wù)器執(zhí)行.

通過(guò)配置[[yii\db\Connection]]可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)復(fù)制和讀寫(xiě)分離.

[
    'class' => 'yii\db\Connection',

    // 配置主服務(wù)器
    'dsn' => 'dsn for master server',
    'username' => 'master',
    'password' => '',

    // 配置從服務(wù)器
    'slaveConfig' => [
        'username' => 'slave',
        'password' => '',
        'attributes' => [
            // use a smaller connection timeout
            PDO::ATTR_TIMEOUT => 10,
        ],
    ],

    // 配置從服務(wù)器組
    'slaves' => [
        ['dsn' => 'dsn for slave server 1'],
        ['dsn' => 'dsn for slave server 2'],
        ['dsn' => 'dsn for slave server 3'],
        ['dsn' => 'dsn for slave server 4'],
    ],
]

以上的配置實(shí)現(xiàn)了一主多從的結(jié)構(gòu),從服務(wù)器用以執(zhí)行讀查詢,主服務(wù)器執(zhí)行寫(xiě)入查詢,讀寫(xiě)分離的功能由后臺(tái)代碼自動(dòng)完成.調(diào)用者無(wú)須關(guān)心.例如:

// 使用以上配置創(chuàng)建數(shù)據(jù)庫(kù)連接對(duì)象
$db = Yii::createObject($config);

// 通過(guò)從服務(wù)器執(zhí)行查詢操作
$rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();

// 通過(guò)主服務(wù)器執(zhí)行更新操作
$db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute();

注意:通過(guò)[[yii\db\Command::execute()]] 執(zhí)行的查詢被認(rèn)為是寫(xiě)操作,所有使用[[yii\db\Command]]來(lái)執(zhí)行的其他查詢方法被認(rèn)為是讀操作.你可以通過(guò)$db->slave得到當(dāng)前正在使用能夠的從服務(wù)器.

Connection組件支持從服務(wù)器的負(fù)載均衡和故障轉(zhuǎn)移,當(dāng)?shù)谝淮螆?zhí)行讀查詢時(shí),會(huì)隨即選擇一個(gè)從服務(wù)器進(jìn)行連接,如果連接失敗則又選擇另一個(gè),如果所有從服務(wù)器都不可用,則會(huì)連接主服務(wù)器。你可以配置[[yii\db\Connection::serverStatusCache|server status cache]]來(lái)記住那些不能連接的從服務(wù)器,使Yii 在一段時(shí)間[[yii\db\Connection::serverRetryInterval].內(nèi)不會(huì)重復(fù)嘗試連接那些根本不可用的從服務(wù)器.

注意:在上述配置中,每個(gè)從服務(wù)器連接超時(shí)時(shí)間被指定為10s. 如果在10s內(nèi)不能連接,則被認(rèn)為該服務(wù)器已經(jīng)掛掉.你也可以自定義超時(shí)參數(shù).

你也可以配置多主多從的結(jié)構(gòu),例如:

[
    'class' => 'yii\db\Connection',

    // 配置主服務(wù)器
    'masterConfig' => [
        'username' => 'master',
        'password' => '',
        'attributes' => [
            // use a smaller connection timeout
            PDO::ATTR_TIMEOUT => 10,
        ],
    ],

    // 配置主服務(wù)器組
    'masters' => [
        ['dsn' => 'dsn for master server 1'],
        ['dsn' => 'dsn for master server 2'],
    ],

    // 配置從服務(wù)器
    'slaveConfig' => [
        'username' => 'slave',
        'password' => '',
        'attributes' => [
            // use a smaller connection timeout
            PDO::ATTR_TIMEOUT => 10,
        ],
    ],

    // 配置從服務(wù)器組
    'slaves' => [
        ['dsn' => 'dsn for slave server 1'],
        ['dsn' => 'dsn for slave server 2'],
        ['dsn' => 'dsn for slave server 3'],
        ['dsn' => 'dsn for slave server 4'],
    ],
]

上述配置制定了2個(gè)主服務(wù)器和4個(gè)從服務(wù)器.Connection組件也支持主服務(wù)器的負(fù)載均衡和故障轉(zhuǎn)移,與從服務(wù)器不同的是,如果所有主服務(wù)器都不可用,則會(huì)拋出異常.

注意:當(dāng)你使用[[yii\db\Connection::masters|masters]]來(lái)配置一個(gè)或多個(gè)主服務(wù)器時(shí),Connection中關(guān)于數(shù)據(jù)庫(kù)連接的其他屬性(例如:dsn, username, password)都會(huì)被忽略.

事務(wù)默認(rèn)使用主服務(wù)器的連接,并且在事務(wù)執(zhí)行中的所有操作都會(huì)使用主服務(wù)器的連接,例如:

// 在主服務(wù)器連接上開(kāi)始事務(wù)
$transaction = $db->beginTransaction();

try {
    // 所有的查詢都在主服務(wù)器上執(zhí)行
    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();
    $db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute();

    $transaction->commit();
} catch(\Exception $e) {
    $transaction->rollBack();
    throw $e;
}

如果你想在從服務(wù)器上執(zhí)行事務(wù)操作則必須要明確地指定,比如:

$transaction = $db->slave->beginTransaction();

有時(shí)你想強(qiáng)制使用主服務(wù)器來(lái)執(zhí)行讀查詢,你可以調(diào)用seMaster()方法.

$rows = $db->useMaster(function ($db) {
    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();
});

你也可以設(shè)置$db->enableSlavesfalse來(lái)使所有查詢都在主服務(wù)器上執(zhí)行.

操作數(shù)據(jù)庫(kù)模式

獲得模式信息

你可以通過(guò) [[yii\db\Schema]]實(shí)例來(lái)獲取Schema信息:

$schema = $connection->getSchema();

該實(shí)例包括一系列方法來(lái)檢索數(shù)據(jù)庫(kù)多方面的信息:

$tables = $schema->getTableNames();

更多信息請(qǐng)參考[[yii\db\Schema]]

修改模式

除了基礎(chǔ)的 SQL 查詢,[[yii\db\Command]]還包括一系列方法來(lái)修改數(shù)據(jù)庫(kù)模式:

  • 創(chuàng)建/重命名/刪除/清空表
  • 增加/重命名/刪除/修改字段
  • 增加/刪除主鍵
  • 增加/刪除外鍵
  • 創(chuàng)建/刪除索引

使用示例:

// 創(chuàng)建表
$connection->createCommand()->createTable('post', [
    'id' => 'pk',
    'title' => 'string',
    'text' => 'text',
]);

完整參考請(qǐng)查看 yii\db\Command.

上一篇:配置下一篇:分頁(yè)