zoukankan      html  css  js  c++  java
  • Yii 数据库相关操作

    CDbConnection: 一个抽象数据库连接
    CDbCommand: SQL statement
    CDbDataReader: 匹配结果集的一行记录
    CDbTransaction:数据库事务

    访问数据库前需要建立数据库连接;使用DAO建立一个抽象数据库链接

    1 $connection = new CDbConnection($dsn, $username, $password);
    2 $connection->active = true;   // 只有激活了连接才可以使用
    3 $connection->active = false;  // 关闭连接

    CDbConnection继承自CApplicationComponent,所以他可以像组件一样在任何地方使用。因此可以这样访问

    Yii::app()->db

    执行SQL语句需要CDbCommand对象,而该对象由CdbConnection::createCommand()返回,因此

    $connection=Yii::app()->db;
    $command=$connection->createCommand($sql);

    如果SQL语句想要完全由自己写,可以这样

    $newSQL = 'SQL语句';
    $command->text=$newSQL;

    CDbCommand对象有两个方法execute()用于非查询SQL执行,而query(),通俗的讲就是用于SELECT查询
    execute()返回的是INSERT, UPDATE and DELETE操作受影响的记录行数
    query()返回一个CDbDataReader对象,使用CDbDataReader对象可以遍历匹配结果集中的所有记录

    $rowCount=$command->execute();  // execute the non-query SQL
    $dataReader=$command->query();  // execute a query SQL // 返回CDbDataReader对像
    $rows=$command->queryAll();     // query and return all rows of result
    $row=$command->queryRow();      // query and return the first row of result
    $column=$command->queryColumn();    // query and return the first column of result
    $value=$command->queryScalar();     // query and return the first field in the first row

    query()返回的是代表结果集的对象而非直接的结果,因此要获取结果集的记录可以这样

    $dataReader=$command->query();
    // CDbDataReader::read()可以一次获取一行数据,到末尾时返回false while(($row=$dataReader->read())!==false) {...}
    // CDbDataReader实现了迭代器接口因此可以使用foreach遍历 foreach($dataReader as $row) {...}
    // 一次性返回所有的记录(数组) $rows=$dataReader->readAll();

    queryXXX() 形式的方法会直接返回匹配的记录集合,当query()不是,他返回一个代表结果集的对象

    YII中的CDbTransaction类用于事务

    // 首先,建立一个连接
    $connection = Yii::app()->db;
    // 第二,开始事务
    $transaction=$connection->beginTransaction();
    // 第三,执行SQL,如果错误就抛出异常,在异常处理中回滚。
    try
    {
        $connection->createCommand($sql1)->execute();
        $connection->createCommand($sql2)->execute();
        //.... other SQL executions
        // 如果SQL执行都没有抛出异常,那就提交。
        $transaction->commit(); 
    } catch(Exception $e) {
        $transaction->rollBack();   // 在异常处理中回滚
    }

    执行SQL中,一般都需要绑定一些用户参数,对于用户参数,需要防止SQL注入攻击
    PDO对象的绑定参数的方法可以防止SQL注入攻击,同样扩展自PDO的DAO也有这样的功能
    举例说明:

    // 第一,建立一个连接:
    $connection = Yii::app()->db;
    // 第二,写下无敌的SQL语句,比如:
    $sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";
    // 第三,创建CDbCommand对象用于执行SQL
    $command=$connection->createCommand($sql);
    // 接下来,将SQL语句中的形式参数,替换为实际参数
    $username = "lxy";
    $email = "lxy@aa.com";
    $command->bindParam(":username", $username, PDO::PARAM_STR);   // 这与PDO有点不同,PDO中不带冒号
    $command->bindParam(":email", $email, PDO::PARAM_STR);    // 同样
    // 最后,执行
    $command->execute();
    // 如果还有其他的数据需要插入,可以再次绑定实参。

    对编译参数的绑定,即对绑定到占位符的参数进行必要的限制如类型,长度等

    PDO_PARAM_BOOL 布尔类型。
    PDO_PARAM_INT 整型。
    PDO_PARAM_STR   char,varchar和其它字符串类型。
    PDO_PARAM_input_output 参数传给存储过程时使用此类型,可以在过程执行后修改。
    PDO_PARAM_NULL NULL类型。
    PDO_PARAM_LOG   大对象类型。
    PDO_PARAM_STMT   PDO对象类型,当前不可操作。
    $sth->bindParam(':calories', $calories, PDO::PARAM_INT);
    $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
    $sth->bindValue(':colour', $colour, PDO::PARAM_STR);

    使用CDbDataReader对象的bindColumn()方法将结果集中的列绑定到PHP变量。

    因此,读取一行记录,列值将自动填充到对应的PHP对象中
    比如这样:

    $connection = Yii::app()->db;
    $sql = "SELECT username, email FROM tbl_user";
    $dataReader = $connection->createCommand($sql)->query();    //很赞的方法链, 可惜不能接着.each()
    $dataReader->bindColumn(1, $username);  //第一列值绑定到$username
    $dataReader->bindColumn(2, $email);     //第二列值绑定到$email
    //接着循环读取并操作数据
    while( $dataReader->read() !== false ) {
        ... // 与先前的 while(($row=$dataReader->read())!==false) 有所不同哦!
    }

    设置表前缀,使用 CDbConnection::tablePrefix 属性在配置文件中设置

    // Yii实现了把一条完整的SQL语句完完全全肢解的能力,比如这样:
    $user = Yii::app()->db->createCommand()
            ->select('id, username, profile')
            ->from('tbl_user u')
            ->join('tbl_profile p', 'u.id=p.user_id')
            ->where('id=:id', array(':id'=>$id))
            ->queryRow(); //返回匹配的结果集的第一行

    其实这条语句是这样的: $newSQL ='SELECT id, username, profile from tbl_user u INNER JOIN tbl_profile p ON u.id = p.user_id WHERE u.id =:id'

    yii提供了一种构建SQL的机制(也就是说不用自己写长长的SQL)
    首相要实例化一个CDbCommand对象

    $command = Yii::app()->db->createCommand(); // 注意参数留空了。。

    可用的方法列表如下:

    ->select(): SELECT子句
    ->selectDistinct(): SELECT子句,并保持了记录的唯一性
    ->from():         构建FROM子句
    ->where():        构建WHERE子句
    ->join():         在FROM子句中构建INNER JOIN 子句
    ->leftJoin():     在FROM子句中构建左连接子句
    ->rightJoin():    在FROM子句中构建右连接子句
    ->crossJoin():    添加交叉查询片段(没用过)
    ->naturalJoin():  添加一个自然连接子片段
    ->group():        GROUP BY子句
    ->having():       类似于WHERE的子句,但要与GROUP BY连用
    ->order():        ORDER BY子句
    ->limit():        LIMIT子句的第一部分
    ->offset():       LIMIT子句的第二部分
    ->union():        appends a UNION query fragment

    select()默认返回全部列

    // 但你可以这样:
    select('username, email');
    // 或使用表限定,或使用别名
    select('tbl_user.id, username name');
    // 或使用数组作为参数
    select(array('id', 'count(*) as num'));

    from() 如果制定了多个表需要使用逗号分隔的字符串,就像原生SQL语句那样:

    from('tbl_user, tbl_post, tbl_profile');
    // 当然,你也可以使用表别名, 还可以使用完整的数据库限定名
    from('tbl_user u, public.tbl_profile p');

    WHERE子句

    // 在where()中使用 AND
    where(array('and', 'id=:id', 'username=:username'), array(':id'=>$id, ':username'=>$username);
    // 在where()中使用 OR 与 AND用法相同,如下:  ##看起来比直接写更加繁琐##
    where( array('and', 'type=1', array('or', 'id=:id','username=:username') ),array(':id'=>$id, ':username'=>$username ));

    IN 操作符用法

    where(array('in', 'id', array(1,2,3)))

    LIKE用法

    where( array('like', 'name', '%tester%') );
    where( array('like','name', array('%test%', '%sample%')) ) // 等于 name LIKE '%test%' AND name LIKE '%sample%
    // 再这样复杂下去, 使用这种方法简直是自杀行为。
    
    $keyword=$ GET['q'];
    // escape % and characters
    $keyword=strtr($keyword, array('%'=>'n%', ' '=>'n '));
    $command->where(array('like', 'title', '%'.$keyword.'%'));

    添加了这么多,你都不知道合成后的SQL长啥样了,可以使用->text()查看(魔术方法)
    如果觉得组合的SQL没有错误,那就执行他,添加->queryAll(); 这可以获得所有匹配的结果集。
    当然,如果你确定执行的结果集中只有一行,可以添加->queryRow();来直接获取。
    如果一个CDbCommand对象需要执行多次,那么在下一次执行之前记得调用reset();

    $command = Yii::app()->db->createCommand();
    $users = $command->select('*')->from('tbl_users')->queryAll();
    $command->reset(); // clean up the previous query
    $posts = $command->select('*')->from('tbl_posts')->queryAll();

     YII的SQL构建函数就是一鸡肋

    // Active Record
    // 使用AR以面向对象的方式访问数据库,AR实现了ORM技术
    // 当Post类表示表tbl_post时,我们可以使用这样的方式插入一条数据
    $post = new Post();
    $post->title = 'new title';
    $post->content = 'new content';
    $post->save();      // 保存即插入

    AR最典型的功能就是执行CRUD操作

    // DAO定位于解决复杂的数据库查询,而AR定位于解决简单的数据库查询
    // 一个AR类代表一张数据表,而一个AR对象代表表中的一行真实的记录,AR类继承CActiveRecord。
    // 如果有一张POST表`tbl_post`,你可以这样定义一个AR类
    class Post extends CActiveRecord
    {
        public static function model($className = __CLASS__)
        {
            return parent::model($className);
        }
    
        public function tablName()
        {
            return 'tbl_post';
        }
    }

    表中的每一个字段都由AR类中的一个属性表示,如果试图通过属性访问表中没有字段,将会抛出一个异常。

    一个AR一定需要一个主键,如果某张表没有主键,你就自己在类中伪造一个,像这样:

    public function primaryKey()
    {
        return 'id';        // 'id' 是关联表中的一个字段,但他不是主键,现在将它指定为主键
    }

    实例化一个AR,填写信息(类似于填充用户提交的信息),然后保存

    $post = new Post;
    $post->title = 'sample post';
    $post->content = 'content for the sample post';
    $post->create_time = time();
    $post->save();  // 保存/插入

    通过AR读取记录 fine() findByPk() findByAttributes() findBySql()

    $post=Post::model()->find($condition,$params);                  // 返回Post对象(如果有匹配记录的话), 否则返回NULL
    $post=Post::model()->findByPk($postID,$condition,$params);
    $post=Post::model()->findByAttributes($attributes,$condition,$params);
    $post=Post::model()->findBySql($sql,$params);

    find()的一个例子

    $post=Post::model()->find('postID=:postID', array(':postID'=>10));

    如果查询条件很是复杂,就要使用CDbCriteria类

    $criteria = new CDbCriteria;
    $criteria->select='title';
    $creteria->condition='postID=:postID';
    $criteria->params=array(':postID'=>10);
    $post=Post::model()->find($criteria);   // 不需要第二个参数
    
    // 另一种更好的写法
    $post=Post::model()->find(array(
        'select' => 'title',
        'condition' => 'postID=:postID',
        'params' => array(':postID' => 10)
    ));

    如果查找的是多行记录可以使用 findAll() findAllByPk()

    findAllByAttributes() findAllBySql()
    // find all rows satisfying the specified condition
    $posts=Post::model()->findAll($condition,$params);
    // find all rows with the specified primary keys
    $posts=Post::model()->findAllByPk($postIDs,$condition,$params);
    // find all rows with the specified attribute values
    $posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
    // find all rows using the specified SQL statement
    $posts=Post::model()->findAllBySql($sql,$params);

    另外的一些可以使用的方法

    // get the number of rows satisfying the specified condition
    $n=Post::model()->count($condition,$params);
    // get the number of rows using the specified SQL statement
    $n=Post::model()->countBySql($sql,$params);
    // check if there is at least a row satisfying the specified condition
    $exists=Post::model()->exists($condition,$params);

    使用AR更新记录

    // 一个典型的实例:
    $post=Post::model()->findByPk(10);
    $post->title='new post title';
    $post->save(); // save the change to database
    // 怎么知道这是一条新纪录还是一条旧的记录呢?使用如下方法:
    if( CActiveRecord::isNewRecord )
    // update the rows matching the specified condition
    Post::model()->updateAll($attributes,$condition,$params);
    // update the rows matching the specified condition and primary key(s)
    Post::model()->updateByPk($pk,$attributes,$condition,$params);
    // update counter columns in the rows satisfying the specified conditions
    Post::model()->updateCounters($counters,$condition,$params);

    删除记录

    $post=Post::model()->findByPk(10); // assuming there is a post whose ID is 10
    $post->delete(); // delete the row from the database table
    // 注意,当删除记录之后,$post仍然可用, 且保留了原始数据。
    // 类级别的方法
    // delete the rows matching the specified condition
    Post::model()->deleteAll($condition,$params);
    // delete the rows matching the specified condition and primary key(s)
    Post::model()->deleteByPk($pk,$condition,$params);

    数据验证

    // 将用户提交的数据保存到AR对象中
    $post->title = $_POST['title'];
    $post->content = $_POST['content'];
    $post->save();
    
    // assume $ POST['Post'] is an array of column values indexed by column names
    $post->attributes=$ POST['Post'];
    $post->save();

    RAR:Relatived Actie Record
    RAR本质上就是执行关系数据查询

    如何让一个AR关联另一个AR
    4中关系类型
    self::BELONGS_TO
    self::HAS_MANY
    self::HAS_ONE
    self::MANY_MANY

    // 关系名称(关系类型,要关联的类名,外键名,其他额外的选项);
    // 定义表关系 类:Post
    public function relations()
    {
        return array(
            'author'=>array(self::BELONGS_TO, 'User', 'author_id'),     // 返回User对象
            'categories'=>array(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)'),
        );
    }

    类:User

    public function relations()
    {
        return array(
                'posts' => array(self::HAS_MANY, 'Post', 'author_id'),
                'profile' => array(self::HAS_ONE, 'Profile', 'owner_id')
        );
    }
    
    // 定义了AR间的关系之后,当执行关系查询时,与AR关联的AR也会自动实例化, 比如这样:
    $author = User::model()->findByPk(1);
    $author->posts;         // posts关系已经定义。

    执行关系查询

    1).lazy loading approach 懒惰关系执行
    // retrieve the post whose ID is 10
    $post=Post::model()->findByPk(10);
    // retrieve the post's author: a relational query will be performed here
    $author=$post->author;  // 如果先前没有执行过,现在才执行这个关系查询(事情拖到这一步才做,真的是很懒啊!)
    
    // 如果关系查询执行后没有匹配的结果,返回将会是NULL或空的数组。
    
    2).eager loading approach   热心的关系查询 //这名字真的很萌!
    // 也就是说一次性取回所有你想要的记录。管你要不要,这这这,太热心了吧!
    $posts=Post::model()->with('author')->findAll();
    // SQL => 'SELECT tbl_post.*, author.* FROM tbl_post t INNER JOIN tbl_user author ON t.author = tbl_user.id'
    
    $posts=Post::model()->with('author','categories')->findAll();
    // SQL => 'SELECT * FROM tbl_post t INNER JOIN tbl_user u ON t.author = u.id INNER JOIN categories c ON t.id = c.post_id'
    
    $posts=Post::model()->with(
        'author.profile',
        'author.posts',
        'categories')->findAll();
    $criteria=new CDbCriteria; $criteria->with=array( 'author.profile', 'author.posts', 'categories', ); $posts=Post::model()->findAll($criteria); 或者 $posts=Post::model()->findAll(array( 'with'=>array( 'author.profile', 'author.posts', 'categories', ) ); // 如果我们想知道用户中谁发过帖子,并且帖子的状态是“公开”。我们并不关心用户发表过的帖子的内容。 $user = User::model()->with('posts')->findAll(); 'VS' $user = User::model()->with(array( 'posts' => array( 'select' => false, 'joinType' => 'INNER JOIN', 'condition' => 'posts.published = 1' ), ))->findAll(); // 返回的将会是所有发过帖子(且帖子已经公开)的用户 // 在relatinos()中定义更加复杂的关系 class User extends CActiveRecord { public function relations() { return array(   'posts'=>array(self::HAS MANY, 'Post', 'author id',   'order'=>'posts.create time DESC',   'with'=>'categories'),   'profile'=>array(self::HAS ONE, 'Profile', 'owner id'), ); } } // 利用别名解决歧义 $posts=Post::model()->with('comments')->findAll(array( 'order'=>'t.create time, comments.create time' )); // Dynamic Relational Query 动态关系SQL查询,更改默认插叙条件: User::model()->with(array( 'posts'=>array('order'=>'posts.create time ASC'), 'profile', ))->findAll(); $user=User::model()->findByPk(1); $posts=$user->posts(array('condition'=>'status=1')); // 返回的都是AR对象, 而不是数据 // 统计查询 class Post extends CActiveRecord { public function relations() { return array( 'commentCount'=>array(self::STAT, 'Comment', 'post_id'), 'categoryCount'=>array(self::STAT, 'Category', 'post_category(post_id, category_id)'), ); } }
  • 相关阅读:
    [转]Android SDK更新 Connection to http://dl-ssl.google.com refused 解决方法
    web学习之Django--安装
    P21(系列四) 线性分类9-朴素贝叶斯分类器(Naive Bayes Classifer)===todo
    P20(系列四) 线性分类8-高斯判别分析(Gaussian Discriminant Analysis)-模型求解(求协方差)===todo
    P19(系列四) 线性分类7-高斯判别分析(Gaussian Discriminant Analysis)-模型求解(求期望)===todo
    P18(系列四) 线性分类7-高斯判别分析(Gaussian Discriminant Analysis)-模型求解(求期望)
    P18(系列四) 线性分类6-高斯判别分析(Gaussian Discriminant Analysis)-模型定义===todo
    P17(系列四) 线性分类5-逻辑回归(Logistic Regression)===todo
    科技竞争的时代已经来临,科技人才如何把握机遇?
    Java与Kotlin, 哪个是开发安卓应用的首选语言?
  • 原文地址:https://www.cnblogs.com/phpfans/p/4063412.html
Copyright © 2011-2022 走看看