Data Access Objects (DAO) 提供了一个通用的 API 以访问存储在不同 DBMS 中的数据. 这样,数据库改变时可以无需修改访问数据库的代码.
Yii DAO 建立于 PHP Data Objects (PDO) ,它是一个为很多 DBMS 提供统一数据访问的扩展, 支持MySQL,PostgreSQL 等. 因此, 要使用 Yii DAO, PDO 扩展和指定的 PDO 数据库驱动 (例如 PDO_MYSQL) 需要被安装.
Yii DAO 主要由下面四个类组成:
- CDbConnection: 代表一个数据库连接.
- CDbCommand: 代表一个执行到数据库的 SQL 语句.
- CDbDataReader: represents a forward-only stream of rows from a query result set.
- CDbTransaction: represents a DB 事务处理.
下面我们介绍在不同场景中 Yii DAO 的用法.
建立数据库连接
要建立一个数据库连接, 需要创建一个 CDbConnection 实例并激活它. 一个数据源名字(DSN) 被用来指定数据库连接信息. 可能也会需要用户名和密码来建立连接.若在连接数据库时出现错误,将会触发一个异常(例如. 错误的 DSN 或无效的 用户名/密码).
1 | $connection = new CDbConnection( $dsn , $username , $password ); |
3 | $connection ->active=true; |
5 | $connection ->active=false; |
DSN 的格式取决于使用的 PDO 数据库驱动. 通常一个 DSN 由 PDO 驱动名字, 跟上一个冒号, 以及驱动专有的连接句法组成. PDO documentation 可以查看到完整信息. 下面是一个常用的 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, 我们也可以使用它作为一个应用组件. 我们可以在应用配置中如下配置 db (或其他名字) 应用组件,来实现此目的
06 | 'class' => 'CDbConnection' , |
07 | 'connectionString' => 'mysql:host=localhost;dbname=testdb' , |
09 | 'password' => 'password' , |
10 | 'emulatePrepare' =>true, |
除非我们明确配置CDbConnection::autoConnect 为 false,否则我们就可以通过已自动被激活的 Yii::app()->db 来访问此 DB 连接, 通过这个方法, 这个单一的 DB 连接可以在代码中的多处位置共享.
执行 SQL 语句
一旦一个数据库连接建立, 就可以使用 CDbCommand 来执行 SQL 语句. 可以通过调用CDbConnection::createCommand()来创建一个CDbCommand 实例,参数是一个 SQL 语句:
1 | $command = $connection ->createCommand( $sql ); |
一个 SQL 语句被执行通过 CDbCommand 以下面两种方式:
- execute(): 执行一个非查询的 SQL 语句, 例如 INSERT, UPDATE 和 DELETE. 若成功执行, 返回影响的记录数目.
- query(): 执行一条返回数据记录的 SQL 语句, 例如 SELECT. 若成功, 返回一个 CDbDataReader 实例. 方便起见, 一些 queryXXX() 方法也可以执行直接以返回查询结果.
若在 SQL 语句查询过程中出现错误,会触发一个异常.
1 | $rowCount = $command ->execute(); |
2 | $dataReader = $command ->query(); |
3 | $rows = $command ->queryAll(); |
4 | $row = $command ->queryRow(); |
5 | $column = $command ->queryColumn(); |
6 | $value = $command ->queryScalar(); |
读取查询结果
在 CDbCommand::query() 产生 CDbDataReader 实例后, 可以通过反复调用CDbDataReader::read()来取得结果集的记录. 也可以在 PHP 的foreach 语言结构中使用CDbDataReader 以逐行检索记录.
1 | $dataReader = $command ->query(); |
3 | while (( $row = $dataReader ->read())!==false) { ... } |
5 | foreach ( $dataReader as $row ) { ... } |
7 | $rows = $dataReader ->readAll(); |
注意: 不同于 query(), 所有 queryXXX() 方法直接返回数据. 例如, queryRow() 返回一个数组,它代表着查询结果中的第一行记录.
使用事务处理
当在一个应用执行一些查询时, 每次读取 和/或 写入数据库中的信息, 确保数据库不是只执行了一部分查询,这一点非常重要. 一个事务处理, 在 Yii 的代表是一个CDbTransaction 实例, may be initiated in this case:
- 开始事务处理.
- 逐个执行查询. 任何对数据库的更新对于外部都是不可见的.
- 提交(Commit)事务. 若事务成功执行,对数据库的更改变得可见.
- 若其中一个查询失败, 整个事务被回滚(rolle back).
上面的流程可以使用下面的代码来执行:
01 | $transaction = $connection ->beginTransaction(); |
04 | $connection ->createCommand( $sql1 )->execute(); |
05 | $connection ->createCommand( $sql2 )->execute(); |
07 | $transaction ->commit(); |
11 | $transaction ->rollBack(); |
绑定参数
为了避免 SQL 注入攻击 和改善执行反复的SQL语句的性能, 你可以"prepare" 一个 SQL 语句,其中的可选参数占位符在参数绑定过程中被实际的数据代替.
参数占位符可以是 命名的(represented as unique tokens) 或 未命名的(represented as question marks). 调用 CDbCommand::bindParam() 或 CDbCommand::bindValue() 以替换这些占位符为实际的参数. 参数无需以引号环绕: 底层数据库驱动为你完成. 参数绑定必须在 SQL 语句被执行前完成.
02 | $sql = "INSERT INTO tbl_user (username, email) VALUES(:username,:email)" ; |
03 | $command = $connection ->createCommand( $sql ); |
05 | $command ->bindParam( ":username" , $username ,PDO::PARAM_STR); |
07 | $command ->bindParam( ":email" , $email ,PDO::PARAM_STR); |
10 | $command ->bindParam( ":username" , $username2 ,PDO::PARAM_STR); |
11 | $command ->bindParam( ":email" , $email2 ,PDO::PARAM_STR); |
方法 bindParam() 和 bindValue() 非常类似. 唯一不同点是前者以一个 PHP 变量引用(reference)绑定一个参数,而后者以一个值绑定一个参数. 对于大量参数(For parameters that represent large block of data memory),为了性能考虑应当使用前者.
关于绑定参数的更详细信息, 查看 相关 PHP 文档.
绑定字段(Binding Columns)
当取出查询结果时, 你也可以绑定字段为 PHP 变量以便它们被每次取出的相应值自动填充.
01 | $sql = "SELECT username, email FROM tbl_user" ; |
02 | $dataReader = $connection ->createCommand( $sql )->query(); |
04 | $dataReader ->bindColumn(1, $username ); |
06 | $dataReader ->bindColumn(2, $email ); |
07 | while ( $dataReader ->read()!==false) |
使用表前缀
从版本 1.1.0 开始, Yii 为使用数据表前缀提供了完整的支持. 表前缀是一个字符串,放置在数据表名字的前面.主要用于共享主机环境,多个应用分享一个数据库,使用不同的表前缀以相互区分. 例如, 一个可以使用 tbl_ 作为表前缀而另一个使用 yii_.
要使用表前缀, 配置 CDbConnection::tablePrefix 属性为你的表前缀. 然后, 在 SQL 语句中使用 {{TableName}} 指向表的名字, TableName 指的是不加前缀的表名字. 例如, 若数据库中有一个名为 tbl_user 的表, 同时tbl_ 被配置为表前缀, 然后我们可以使用下面的代码查询用户:
1 | $sql = 'SELECT * FROM {{user}}' ; |
2 | $users = $connection ->createCommand( $sql )->queryAll(); |