Yii学习总结之数据访问对象 (DAO)


Posted in PHP onFebruary 22, 2015

Yii提供了强大的数据库编程支持。Yii数据访问对象(DAO)建立在PHP的数据对象(PDO)extension上,使得在一个单一的统一的接口可以访问不同的数据库管理系统(DBMS)。使用Yii的DAO开发的应用程序可以很容易地切换使用不同的数据库管理系统,而不需要修改数据访问代码。

数据访问对象(DAO) 对访问存储在不同数据库管理系统(DBMS)中的数据提供了一个通用的API。 因此,在将底层 DBMS 更换为另一个时,无需修改使用了 DAO 访问数据的代码。

Yii DAO 基于 PHP Data Objects (PDO) 构建。它是一个为众多流行的DBMS提供统一数据访问的扩展,这些 DBMS 包括 MySQL, PostgreSQL 等等。因此,要使用 Yii DAO,PDO 扩展和特定的 PDO 数据库驱动(例如PDO_MYSQL) 必须安装。

Yii DAO 主要包含如下四个类:

CDbConnection: 代表一个数据库连接。
CDbCommand: 代表一条通过数据库执行的 SQL 语句。
CDbDataReader: 代表一个只向前移动的,来自一个查询结果集中的行的流。
CDbTransaction: 代表一个数据库事务。
下面,我们介绍 Yii DAO 在不同场景中的应用。

1. 建立数据库连接
要建立一个数据库连接,创建一个 CDbConnection 实例并将其激活。 连接到数据库需要一个数据源的名字(DSN)以指定连接信息。用户名和密码也可能会用到。 当连接到数据库的过程中发生错误时 (例如,错误的 DSN 或无效的用户名/密码),将会抛出一个异常。

$connection=new CDbConnection($dsn,$username,$password);

// 建立连接。你可以使用  try...catch 捕获可能抛出的异常

$connection->active=true;

......

$connection->active=false;  // 关闭连接

DSN 的格式取决于所使用的 PDO 数据库驱动。总体来说, DSN 要含有 PDO 驱动的名字,跟上一个冒号,再跟上驱动特定的连接语法。可查阅 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,我们也可以将其作为一个 应用组件 使用。要这样做的话, 请在 应用配置 中配置一个 db (或其他名字)应用组件如下:

array(

    ......

    'components'=>array(

        ......

        'db'=>array(

            'class'=>'CDbConnection',

            'connectionString'=>'mysql:host=localhost;dbname=testdb',

            'username'=>'root',

            'password'=>'password',

            'emulatePrepare'=>true,  // needed by some MySQL installations

        ),

    ),

)

然后我们就可以通过 Yii::app()->db 访问数据库连接了。它已经被自动激活了,除非我们特意配置了CDbConnection::autoConnect 为 false。通过这种方式,这个单独的DB连接就可以在我们代码中的很多地方共享。

2. 执行 SQL 语句
数据库连接建立后,SQL 语句就可以通过使用 CDbCommand 执行了。你可以通过使用指定的SQL语句作为参数调用 CDbConnection::createCommand() 创建一个 CDbCommand 实例。

$connection=Yii::app()->db;   // 假设你已经建立了一个 "db" 连接

// 如果没有,你可能需要显式建立一个连接:

// $connection=new CDbConnection($dsn,$username,$password);

$command=$connection->createCommand($sql);

// 如果需要,此 SQL 语句可通过如下方式修改:

// $command->text=$newSQL;

一条 SQL 语句会通过 CDbCommand 以如下两种方式被执行:

execute(): 执行一个无查询 (non-query)SQL语句, 例如 INSERT, UPDATE 和 DELETE 。如果成功,它将返回此执行所影响的行数。

query(): 执行一条会返回若干行数据的 SQL 语句,例如 SELECT。 如果成功,它将返回一个CDbDataReader 实例,通过此实例可以遍历数据的结果行。为简便起见, (Yii)还实现了一系列queryXXX() 方法以直接返回查询结果。

执行 SQL 语句时如果发生错误,将会抛出一个异常。

$rowCount=$command->execute();   // 执行无查询 SQL

$dataReader=$command->query();   // 执行一个 SQL 查询

$rows=$command->queryAll();      // 查询并返回结果中的所有行

$row=$command->queryRow();       // 查询并返回结果中的第一行

$column=$command->queryColumn(); // 查询并返回结果中的第一列

$value=$command->queryScalar();  // 查询并返回结果中第一行的第一个字段

3. 获取查询结果
在 CDbCommand::query() 生成 CDbDataReader 实例之后,你可以通过重复调用 CDbDataReader::read() 获取结果中的行。你也可以在 PHP 的 foreach 语言结构中使用 CDbDataReader 一行行检索数据。

$dataReader=$command->query();

// 重复调用 read() 直到它返回 false

while(($row=$dataReader->read())!==false) { ... }

// 使用 foreach 遍历数据中的每一行

foreach($dataReader as $row) { ... }

// 一次性提取所有行到一个数组

$rows=$dataReader->readAll();

注意: 不同于 query(), 所有的 queryXXX() 方法会直接返回数据。 例如, queryRow() 会返回代表查询结果第一行的一个数组。

4. 使用事务
当一个应用要执行几条查询,每条查询要从数据库中读取并/或向数据库中写入信息时, 保证数据库没有留下几条查询而只执行了另外几条查询是非常重要的。 事务,在 Yii 中表现为 CDbTransaction 实例,可能会在下面的情况中启动:

开始事务.
一个个执行查询。任何对数据库的更新对外界不可见。
提交事务。如果事务成功,更新变为可见。
如果查询中的一个失败,整个事务回滚。
上述工作流可以通过如下代码实现:

$transaction=$connection->beginTransaction();

try

{

    $connection->createCommand($sql1)->execute();

    $connection->createCommand($sql2)->execute();

    //.... other SQL executions

    $transaction->commit();

}

catch(Exception $e) // 如果有一条查询失败,则会抛出异常

{

    $transaction->rollBack();

}

5. 绑定参数
要避免 SQL 注入攻击 并提高重复执行的 SQL 语句的效率, 你可以 "准备(prepare)"一条含有可选参数占位符的 SQL 语句,在参数绑定时,这些占位符将被替换为实际的参数。

参数占位符可以是命名的 (表现为一个唯一的标记) 或未命名的 (表现为一个问号)。调用CDbCommand::bindParam() 或 CDbCommand::bindValue() 以使用实际参数替换这些占位符。 这些参数不需要使用引号引起来:底层的数据库驱动会为你搞定这个。 参数绑定必须在 SQL 语句执行之前完成。

// 一条带有两个占位符 ":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();

// 使用新的参数集插入另一行

$command->bindParam(":username",$username2,PDO::PARAM_STR);

$command->bindParam(":email",$email2,PDO::PARAM_STR);

$command->execute();

方法 bindParam() 和 bindValue() 非常相似。唯一的区别就是前者使用一个 PHP 变量绑定参数, 而后者使用一个值。对于那些内存中的大数据块参数,处于性能的考虑,应优先使用前者。

关于绑定参数的更多信息,请参考 相关的PHP文档。

6. 绑定列
当获取查询结果时,你也可以使用 PHP 变量绑定列。 这样在每次获取查询结果中的一行时就会自动使用最新的值填充。

$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 

}

7. 使用表前缀
从版本 1.1.0 起, Yii 提供了集成了对使用表前缀的支持。 表前缀是指在当前连接的数据库中的数据表的名字前面添加的一个字符串。 它常用于共享的服务器环境,这种环境中多个应用可能会共享同一个数据库,要使用不同的表前缀以相互区分。 例如,一个应用可以使用 tbl_ 作为表前缀而另一个可以使用 yii_。

要使用表前缀,配置 CDbConnection::tablePrefix 属性为所希望的表前缀。 然后,在 SQL 语句中使用{{TableName}} 代表表的名字,其中的 TableName 是指不带前缀的表名。 例如,如果数据库含有一个名为tbl_user 的表,而 tbl_ 被配置为表前缀,那我们就可以使用如下代码执行用户相关的查询:

$sql='SELECT * FROM {{user}}';

$users=$connection->createCommand($sql)->queryAll();
PHP 相关文章推荐
Apache2 httpd.conf 中文版
Dec 06 PHP
MySQL授权问题总结
May 06 PHP
深入解析php中的foreach问题
Jun 30 PHP
深入Nginx + PHP 缓存详解
Jul 11 PHP
php 批量替换程序的具体实现代码
Oct 04 PHP
php读取excel文件示例分享(更新修改excel)
Feb 27 PHP
php根据日期显示所在星座的方法
Jul 13 PHP
php中ob函数缓冲机制深入理解
Aug 03 PHP
PHP crc32()函数讲解
Feb 14 PHP
php设计模式之代理模式分析【星际争霸游戏案例】
Mar 23 PHP
PHP中->和=>的含义及使用示例解析
Aug 06 PHP
php解析非标准json、非规范json的方式实例
May 10 PHP
Yii学习总结之安装配置
Feb 22 #PHP
使用php的HTTP请求的库Requests实现美女图片墙
Feb 22 #PHP
十幅图告诉你什么是PHP引用
Feb 22 #PHP
PHP的引用详解
Feb 22 #PHP
PHP实现加密的几种方式介绍
Feb 22 #PHP
php使用parse_url和parse_str解析URL
Feb 22 #PHP
php的闭包(Closure)匿名函数详解
Feb 22 #PHP
You might like
php中file_get_contents与curl性能比较分析
2014/11/08 PHP
关于WordPress的SEO优化相关的一些PHP页面脚本技巧
2015/12/10 PHP
thinkPHP订单数字提醒功能的实现方法
2016/12/01 PHP
php面向对象之反射功能与用法分析
2017/03/29 PHP
微信公众号实现会员卡领取功能
2017/06/08 PHP
PHP7 其他语言层面的修改
2021/03/09 PHP
PHP7新增函数
2021/03/09 PHP
jsp js鼠标移动到指定区域显示选项卡离开时隐藏示例
2013/06/14 Javascript
jQuery UI 实现email输入提示实例
2013/08/15 Javascript
js操纵dom生成下拉列表框的方法
2014/02/24 Javascript
理解javascript回调函数
2014/12/28 Javascript
vuejs2.0运用原生js实现简单的拖拽元素功能示例
2017/02/24 Javascript
bootstrap实现二级下拉菜单效果
2017/11/23 Javascript
关于Angularjs中自定义指令一些有价值的细节和技巧小结
2018/04/22 Javascript
使用JavaScript生成罗马字符的实例代码
2018/06/08 Javascript
VUE v-for循环中每个item节点动态绑定不同函数的实例
2018/09/26 Javascript
详解一个基于套接字实现长连接的express
2019/03/28 Javascript
使用vue实现各类弹出框组件
2019/07/03 Javascript
JAVA面试题 static关键字详解
2019/07/16 Javascript
Element DateTimePicker日期时间选择器的使用示例
2020/07/27 Javascript
vue编写简单的购物车功能
2021/01/08 Vue.js
[01:03:33]Alliance vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
请不要重复犯我在学习Python和Linux系统上的错误
2016/12/12 Python
django 解决扩展自带User表遇到的问题
2020/05/14 Python
CSS3 选择器 属性选择器介绍
2012/01/21 HTML / CSS
简述数据库的设计过程
2015/06/22 面试题
电大毕业自我鉴定
2014/02/03 职场文书
会计自我鉴定
2014/02/04 职场文书
办公室主任岗位承诺书
2014/05/29 职场文书
2014党委书记四风对照检查材料思想汇报
2014/09/21 职场文书
领导班子对照检查剖析材料
2014/10/13 职场文书
2015年挂职锻炼工作总结
2014/12/12 职场文书
毕业欢送晚会主持词
2019/06/25 职场文书
励志正能量20句:送给所有为梦想拼搏的人
2019/11/11 职场文书
Python 数据结构之十大经典排序算法一文通关
2021/10/16 Python
Python进程间的通信之语法学习
2022/04/11 Python