ActiveRecord使用的一个陷阱导致 Invalid parameter number: no parameters were bound

请看下面的例子

$criteria = new CDbCriteria();
$criteria->select = "*";
$model = Biubiu::model();
$ids = range(160,163);
$criteria->addInCondition("id", $ids);
$model->findByPk(160);//某次操作
sleep(32);//处理其他事情花费了较长时间。
$result = $model->findAll($criteria);<1>
//$result = $model->getCommandBuilder()->createFindCommand($model->getTableSchema(),$criteria)->queryAll();<2>
if($result){
echo count($result);
}

为了体现这个问题,我的本地数据库wait_timeout = 30

那么会出现下面的问题:

exception 'Exception' with message 'exception 'CDbException' with message 'CDbCommand failed to execute the SQL statement: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound. The SQL statement executed was: SELECT * FROM `t_biubiu` `t` WHERE id IN (:ycp0, :ycp1, :ycp2, :ycp3). Bound with :ycp0=160, :ycp1=161, :ycp2=162, :ycp3=163' in E:\xxxx\framework\db\CDbCommand.php:569
Stack trace:
#0 E:\xxxx\framework\db\CDbCommand.php(578): CDbCommand->queryInternalAll('fetchAll', Array, Array)
#1 E:\xxxx\framework\db\CDbCommand.php(418): CDbCommand->queryInternal('fetchAll', Array, Array)
#2 E:\xxxx\framework\db\ar\CActiveRecord.php(1356): CDbCommand->queryAll()
#3 E:\xxxx\framework\db\ar\CActiveRecord.php(1475): CActiveRecord->query(Object(CDbCriteria), true)
#4 E:\xxxx\experiment\protected\commands\YiiCommand.php(18): CActiveRecord->findAll(Object(CDbCriteria))

我的另一片文章和这个问题的原因都是一样的:Yii 数据库重连告别General error: 2006 MySQL server has gone away

以我所见的解决方法就是 :
1—— 尽量避免上面的这种写法,直接addInCondition,把id填入SQL

    $criteria->addCondition("id in (". join(",",$ids) . ")");

2——就是重连。

分析:

并不是参数没有绑定,查看findAll()的源码

public function findAll($condition='',$params=array())
{
Yii::trace(get_class($this).'.findAll()','system.db.ar.CActiveRecord');
$criteria=$this->getCommandBuilder()->createCriteria($condition,$params);
return $this->query($criteria,true);
}

query实际上执行的是:

$command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria);
$command->queryAll();

而createFindCommand()
调用bindValue(),里面的代码如下:

$this->_statement->bindValue($name,$value,$this->_connection->getPdoType(gettype($value)));

连接已经timeout,失效了。bindValue无用。

queryAll()  --> queryInternal('fetchAll',PDO::FETCH_ASSOC,[]);
-->三次调用 queryInternalAll('fetchAll',PDO::FETCH_ASSOC,[])

简化的queryInternalAll如下:

private function queryInternalAll($method,$mode,$params=array())
{
$params=array_merge($this->params,$params);
try
{
$this->prepare();
@$this->_statement->execute();
{
$mode=(array)$mode;
call_user_func_array(array($this->_statement, 'setFetchMode'), $mode);
$result=$this->_statement->$method();
$this->_statement->closeCursor();
}
return $result;
}
catch(Exception $e)
{
$errorInfo=$e instanceof PDOException ? $e->errorInfo : null;
$message=$e->getMessage(); if(YII_DEBUG)
$message.='. The SQL statement executed was: '.$this->getText().$par; if(!empty($errorInfo) && (2006 == $errorInfo[1] || 2013 == $errorInfo[1])) {
$this->_connection->setActive(false);
$this->cancel();
}
throw new CDbException(Yii::t('yii','CDbCommand failed to execute the SQL statement: {error}',
array('{error}'=>$message)),(int)$e->getCode(),$errorInfo);
}
}

这样就看到了

exception ‘Exception’ with message ‘exception ‘CDbException’ with message ‘CDbCommand failed to execute the SQL statement: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound. The SQL statement executed was:

另外:设置db属性:

'attributes' => [
PDO::ATTR_TIMEOUT => 600,
],

是没有用处的。
但可以考虑使用:

Yii::$app->db->createCommand('SET SESSION wait_timeout = 28800;')->execute();

重试机制:

//Biubiu::getDb()->close(); 这么搞也行但是每次都会关闭。
do{
try{
$result = Biubiu::find()->select("id,value")->where(['id'=>$ids])->all();
foreach ($result as $one){
echo $one->id . ">" .$one->value . PHP_EOL;
}
}catch (\yii\db\Exception $e){
Biubiu::getDb()->close();
Biubiu::getDb()->open();
if(strpos($e->getMessage(), 'MySQL server has gone away')===false){
throw new Exception($e);
}
}
}while(--$retry);

执行时间较长的脚本,并且一段时间就会结束的,不能用持久化连接PDO::ATTR_PERSISTENT => true,适合用这种做法进行重连,能有效防止闪断等超时错误。

前面提到的文章给出的解决办法并不适合大量反复的数据库访问,会多很多不必要的ping操作。然而就几千条的数据,就不必在乎其带来的性能影响。

源码剖析Yii错误 Invalid parameter number: no parameters were bound的更多相关文章

  1. Django----djagorest-framwork源码剖析

    restful(表者征状态转移,面向资源编程)------------------------------------------->约定 从资源的角度审视整个网络,将分布在网络中某个节点的资源 ...

  2. Django Rest Framework源码剖析(七)-----分页

    一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...

  3. Django Rest Framework源码剖析(四)-----API版本

    一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...

  4. 玩转Android之Picasso使用详详详详详详解,从入门到源码剖析!!!!

    Picasso是Squareup公司出的一款图片加载框架,能够解决我们在Android开发中加载图片时遇到的诸多问题,比如OOM,图片错位等,问题主要集中在加载图片列表时,因为单张图片加载谁都会写.如 ...

  5. 豌豆夹Redis解决方案Codis源码剖析:Dashboard

    豌豆夹Redis解决方案Codis源码剖析:Dashboard 1.不只是Dashboard 虽然名字叫Dashboard,但它在Codis中的作用却不可小觑.它不仅仅是Dashboard管理页面,更 ...

  6. RestFramework——API基本实现及dispatch基本源码剖析

    基于Django实现 在使用RestFramework之前我们先用Django自己实现以下API. API完全可以有我们基于Django自己开发,原理是给出一个接口(URL),前端向URL发送请求以获 ...

  7. 侯捷STL课程及源码剖析学习1

    1.C++标准库和STL C++标准库以header files形式呈现: C++标准库的header files不带后缀名(.h),例如#include <vector> 新式C hea ...

  8. WorldWind源码剖析系列:星球球体的加载与渲染

    WorldWind源码剖析系列:星球球体的加载与渲染 WorldWind中主函数Main()的分析 在文件WorldWind.cs中主函数Main()阐明了WorldWind的初始化运行机制(如图1所 ...

  9. PCL源码剖析之MarchingCubes算法

    原文:http://blog.csdn.net/lming_08/article/details/19432877 MarchingCubes算法简介 MarchingCubes(移动立方体)算法是目 ...

随机推荐

  1. 快速入门和使用HTML–使用Django建立你的第一个网站

    一 前记 你每天浏览的网页,通过网络看的新闻,看着淘宝京东的绚丽多彩的界面.是否想过这个问题,它是怎么实现的呢?有没有搜过相关的知识呢?假如没有,假如你是一位对事物好奇的主或者是做计算机相关东西的人. ...

  2. json与java对象的转换,以及struts2对json的支持,实现ajax技术

    这两天学的东西有点多,今天抽个时间写下来,以此作为激励,这两天学了json,ajax,jQuery 一.使用第三方的工具java转换为json类型 首先就是java类型转换为json对象,首先要导入第 ...

  3. 如何解决myeclipse2014突然无法打开的问题

    今天突然发现我的myeclipse2014打开不了,昨晚还好好的,上网找了一下没有找到解决方法,于是新建一个工作区间Workspace Test,再打开File->Switch WorkSpac ...

  4. MySQL中日期和时间戳互相转换的函数和方法

     时间戳转换成日期 复制代码代码如下: FROM_UNIXTIME 例如: 数据表中 invest_time 存储的是时间戳,如 1429063399 使用 FROM_UNIXTIME 可以把时间戳转 ...

  5. springcloud --- spring cloud sleuth和zipkin日志管理(spring boot 2.18)

    前言 在spring cloud分布式架构中,系统被拆分成了许多个服务单元,业务复杂性提高.如果出现了异常情况,很难定位到错误位置,所以需要实现分布式链路追踪,跟进一个请求有哪些服务参与,参与的顺序如 ...

  6. windows下安装和设置gradle

    一.安装前检查 检查jdk是否已经安装 二.下载gradle 1. https://gradle.org/releases/ 2.设置gradle环境变量 3. 环境变量中增加名为GRADLE_HOM ...

  7. sbt 学习笔记(1)sbt安装和交互式界面使用

    下载sbt: http://www.scala-sbt.org/download.html 解压zip文件F:\sbt-0.13.15 配置环境变量 如果需要可以修改F:\sbt-0.13.15\sb ...

  8. 使用 Chrome 对长网页(知乎、微信公众号文章)进行完整截图

    当需要对一个较长的网页进行完整截图时,可以直接使用谷歌浏览器(Chrome)自带的截图功能完成,不需要依赖第三方截图软件. 1. 打开网页 以微信公众号的页面作为示例:https://mp.weixi ...

  9. win10安装python

    下载地址:https://www.python.org/downloads/release/python-365/ 安装完成后,在cmd里输入  python ,检查是否安装成功

  10. C# WinForm 跨线程访问控件(实用简洁写法)

    C# WinForm 跨线程访问控件(实用简洁写法) 1.<C# WinForm 跨线程访问控件(实用简洁写法)>       2.<基于.NET环境,C#语言 实现 TCP NAT ...