Yii2学习之DAO和Query
来源:互联网 发布:如何测试网络丢包 编辑:程序博客网 时间:2024/05/16 10:32
简单介绍
上节介绍了AR类,是以面向对象的方式操作数据库,yii2还提供了一套统一的数据库访问层API(Data Access Objects),可以以SQL语句方式操作数据库表,能完成AR类的任何操作,但是相对与AR使用DAO需要写大量的sql语句,如果你的sql语句很复杂,会在后续工作中很难维护,所以一般建议还是使用AR类。但是不管DAO还是AR他们都是通过Query基类创建sql语句(AR类通过创建AQ对象操作),最后都是通过Command类的queryInternal方法的PDO操作返回查询的数据。
源码解析
基本的DAO操作 $query = Yii::$app->db->createCommand(“select * from oc_user where id= :id", [':id' => $id])->queryOne();
Query查询返回数组
$query = (new Query()) ->select('*') ->from('oc_user') ->where('id = :id', [':id' => $id]) ->one();
上面两种格式返回oc_user中id为指定
public function createCommand($sql = null, $params = []) { $command = new Command([ 'db' => $this, 'sql' => $sql, ]); return $command->bindValues($params); }
createCommand只是创建了个Command类,将db连接实例对象和sql查询语句保存到对应的属性中,返回创建的Command类对象。
public $db; private $_sql;
然后调用Command类的queryOne方法调用到queryInternal方法返回查询语句对应的数组数据(查询成功)
protected function queryInternal($method, $fetchMode = null) { $rawSql = $this->getRawSql(); Yii::info($rawSql, 'yii\db\Command::query'); if ($method !== '') { $info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency); if (is_array($info)) { /* @var $cache \yii\caching\Cache */ $cache = $info[0]; $cacheKey = [ __CLASS__, $method, $fetchMode, $this->db->dsn, $this->db->username, $rawSql, ]; $result = $cache->get($cacheKey); if (is_array($result) && isset($result[0])) { Yii::trace('Query result served from cache', 'yii\db\Command::query'); return $result[0]; } } } $this->prepare(true);//该方法中执行$pdo->prepare($sql); $token = $rawSql; try { Yii::beginProfile($token, 'yii\db\Command::query'); $this->pdoStatement->execute(); if ($method === '') { $result = new DataReader($this); } else { if ($fetchMode === null) { $fetchMode = $this->fetchMode; } $result = call_user_func_array([$this->pdoStatement, $method], (array) $fetchMode); $this->pdoStatement->closeCursor(); } Yii::endProfile($token, 'yii\db\Command::query'); } catch (\Exception $e) { Yii::endProfile($token, 'yii\db\Command::query'); throw $this->db->getSchema()->convertException($e, $rawSql); } if (isset($cache, $cacheKey, $info)) { $cache->set($cacheKey, [$result], $info[1], $info[2]); Yii::trace('Saved query result in cache', 'yii\db\Command::query'); } return $result; }
真正执行查询语句是在$this->pdoStatement->execute();
后,queryOne方法的$method是fetch,固在call_user_func_array中调用PDO.php的fetch方法采集查询的数据,最后以数组形式返回。
Query的查询与createCommand类似,只不过中间多了步QueryBuilder的解析,解析每一个方法中字段的值组合成sql语句,最后还是调用Command对象的queryOne方法去查询。
public function select($columns, $option = null){ if (!is_array($columns)) { $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); } $this->select = $columns; $this->selectOption = $option; return $this;}
public function where($condition, $params = []){ $this->where = $condition; $this->addParams($params); return $this;}
public function one($db = null){ return $this->createCommand($db)->queryOne();}
public function createCommand($db = null){ if ($db === null) { $db = Yii::$app->getDb(); } list ($sql, $params) = $db->getQueryBuilder()->build($this); return $db->createCommand($sql, $params);}
Query中的每个操作方法都是返回$this对象本身(select,from,where等等),在one方法时创建了QueryBuiler类通过build方法解析Query对象的数据,然后执行db连接对象的createCommand将sql语句和db对象复制给Command对象,然后调用Command对象的queryOne执行查询sql语句返回结果,后面流程同上。
常用操作
//返回id为1对应的行 $user = Yii::$app->db->createCommand('SELECT * FROM oc_user WHERE id=1')->queryOne();
//返回oc_user表的所有数据 $user = Yii::$app->db->createCommand('SELECT * FROM oc_user')->queryAll();
//返回oc_user表的name列 $user = Yii::$app->db->createCommand('SELECT name FROM oc_user')->queryColumn();
//返回id为1对应行的第一列的值 $user = Yii::$app->db->createCommand('SELECT * FROM oc_user WHERE id=1')->queryScalar();
//删除id为1的一行1 Yii::$app->db->createCommand()->delete('oc_user', 'id = 1')->execute();
//引用变量时不要直接写到sql语句中,安全性 $user = Yii::$app->db->createCommand("SELECT * FROM oc_user WHERE id=$id")->queryOne();
不安全,如果别人攻击你服务器,$id传入一个delete sql语句很有可能会把你的数据表删除掉 $user = Yii::$app->db->createCommand("SELECT * FROM oc_user WHERE id=:id", [':id' => $id])->queryOne();
改成这种格式,createCommand会以bindValues绑定参数,在调用PDO操作时是会从_pendingParams读取相应的值以bindValue操作查询。(这个要看PDO.php的bindValue这个处理原理,应该会去做检测,有时间可以研究下)
public function bindValues($values){ if (empty($values)) { return $this; } $schema = $this->db->getSchema(); foreach ($values as $name => $value) { if (is_array($value)) { $this->_pendingParams[$name] = $value; $this->params[$name] = $value[0]; } else { $type = $schema->getPdoType($value); $this->_pendingParams[$name] = [$value, $type]; $this->params[$name] = $value; } } return $this;}
//事物操作,执行多个数据操作时,封装到一个事物中可以保证数据的一致性,如果中间操作失败可以回滚到操作之前的位置,防止数据出现一个保存,一个失败等问题。
$transaction = $connection->beginTransaction();try { $connection->createCommand($sql1)->execute(); $connection->createCommand($sql2)->execute(); // ... 执行其他 SQL 语句 ... $transaction->commit();} catch(Exception $e) { $transaction->rollBack();}
Query高级查询
query的where方法支持多种格式(1、纯字符串 2、哈希格式 3、操作符格式)
//字符串 ->Where('state = 1')
//哈希格式 ->Where(['state' => 1])
//操作符格式 ->Where(['in', 'state', [1,2,3]])
//常用操作符格式
->where(['between', 'state', 1, 10])->where(['and', 'id=1', 'type=1'])->filterWhere(['like', 'name', 'tester'])->andWhere(['>', 'age', 10])
//order by id ASC ->orderBy('id ASC')
//group by 分组查询 ->groupBy(['id', 'status']);
//过滤 ->having(['status' => 1]);
//从第20条开始读取10条数据 ->limit(10)->offset(20);
//从第10条开始读取20条数据 ->limit('10, 20')
//联合查找 ->leftJoin('post', 'post.user_id = user.id');
//union组合
$query1 = (new \yii\db\Query()) ->select("id, category_id AS type, name") ->from('post') ->limit(10);$query2 = (new \yii\db\Query()) ->select('id, type, name') ->from('user') ->limit(10);$query1->union($query2);
最后调用->one(); ->all();
执行查询操作返回需要的数据。
//批量处理,一次获取一条查询 foreach ($query->each() as $user)
例子解析:
$subQuery = (new Query()) ->select('*') ->from('oc_task_state_log') ->where(['in', 'from_state', [OcButlerTask::BUTLER_TASK_ASSIGN, OcButlerTask::BUTLER_TASK_CONTACT, OcButlerTask::BUTLER_TASK_UNSUCCESS_POTENTIAL, OcButlerTask::BUTLER_TASK_CHANGE_TIME]]) ->andWhere(['in', 'to_state', [OcButlerTask::BUTLER_TASK_EARNEST_MONEY, OcButlerTask::BUTLER_TASK_DIRECT]]) ->andWhere(['between', 'create_at', $start_time, $end_time]);$query = (new Query()) ->select('a.create_at, a.task_id, oc_butler.nick_name as nick_name') ->from(['a' => $subQuery]) ->leftJoin('oc_butler_task', 'a.task_id = oc_butler_task.id') ->leftJoin('oc_earnest_money', 'oc_earnest_money.id = oc_butler_task.earnest_money_id') ->leftJoin('oc_butler', 'oc_butler.id = oc_butler_task.butler_id');$query->andWhere('oc_butler.nick_name = :nick_name', [':nick_name' => $params['nick_name']]);$query->orderBy('create_at DESC');foreach($query->each(10) as $task_log){ //...}
- Yii2学习之DAO和Query
- yii2学习之始
- Yii2学习之ActiveRecord
- 【Yii2】yii2学习之CSRF验证
- Yii2 数据操作DAO
- yii2学习之CSRF验证
- yii2学习之CSRF验证
- hibernate学习之四——Query和Criteria接口
- Yii2.0学习笔记(一)之Yii2.0起步
- yii2.0数据库交互DAO
- Yii2 数据操作Query Builder
- Yii2 数据操作Query Builder
- Yii2 数据操作Query Builder
- Yii2 数据操作Query Builder
- Yii2 数据操作Query Builder
- Yii2 数据操作Query Builder
- YII2 query的数据库操作
- Yii2 给ListView用dao方式进行赋值,排序和分页
- hdu5305 Friends
- HDU 1257 最少拦截系统
- RelativeLayout.LayoutParams参数
- 编码不一致问题-Illegal mix of collations
- HDU 1166 敌兵布阵-线段树-(单点更新,区间查询)
- Yii2学习之DAO和Query
- 菜鸟成长日志-SMTP&POP3邮件收发协议
- LeetCode 242 Valid Anagram
- python 读取本地txt,存入到mysql
- 三个月-见识菜鸟的每天的成长(js正则表达式&&phpmail的收发)
- HNU 13365 Circle of digits Input hou
- 双向循环链表
- HDU1521 排列组合【指数型母函数】
- Primary Key & Index