Yii MYSQL PDO 避坑

kyle 2022-10-28 2318次浏览 82条评论 0 打赏作者 1 0

运行环境:

PHP7.1.X
MySQL5.7.x 、MySQL8.0.x

Yii db配置:

'db' => [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=;port=;dbname=',
    'username' => '',
    'password' => '',
    'charset' => '',
    'tablePrefix' => '',
    'attributes'  => [
        PDO::ATTR_STRINGIFY_FETCHES => false, // 提取的时候是否将数值转换为字符串
        PDO::ATTR_EMULATE_PREPARES  => false, // 启用或禁用预处理语句的模拟
        PDO::ATTR_CASE => PDO::CASE_LOWER, // 强制列名为指定的大小写
    ],
],

情景描述:
项目中难免会遇到嵌套事务的情况,当我在使用本身有开启事务的主方法中调用了一个同样存在事务的子方法时出现了如下的错误提示:

SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered 
queries are active. Consider using PDOStatement::fetchAll(). Alternatively, 
if your code is only ever going to run against mysql, you may enable query buffering 
by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
The SQL being executed was: SAVEPOINT LEVEL1

最后它给出了出现问题的sql语句,即SAVEPOINT LEVEL1的时候。

我通过这句话给的解决方案,在db配置文件中,设置PDO::MYSQL_ATTR_USE_BUFFERED_QUERY属性为true,发现无效。

再然后,我查到设置PDO::ATTR_EMULATE_PREPARES为false可以解决该问题,于是我尝试了下,的确可以,想想就很开心,终于解决了。

但是,如果把这个属性设置为false,那么,接下来从数据查询出来的 int 整型 类型的数据就会变成 string 字符串 类型,这个,就尴尬了。难道没有两全的办法吗?
默认情况下,PDO是会将int转换成string的

PDO::ATTR_STRINGIFY_FETCHES 提取的时候将数值转换为字符串,默认为true。 
PDO::ATTR_EMULATE_PREPARES 启用或禁用预处理语句的模拟,默认为true。

为了保证整型还是整型,请设置上述两项属性为false。

但是部分客户端又需要严格区分字符类型(整型、字符串、xxx)。若PDO::ATTR_EMULATE_PREPARES改成true可以解决问题,那么就在需要该值为true的时候改成true,然后操作完毕后将其改回false。这样确实可以,但是代码冗余,也增加工作量。如果是一个老项目,那且不是很多地方都得去改?

// Yii2.0设置PDO属性的语法
Yii::$app->db->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, 1); 
// 存在savepoint level1可能的代码逻辑
 if($FilmFollow->updateFollow($filmId, $epNum) === false) {
   $this->addError('', '0:追剧失败');
   return false;
 }
// 改回false
Yii::$app->db->pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, 0);


最终解决方案:升级PHP版本(PHP7.1.X --> PHP7.4.X)

0

0 条评论

    没有找到数据。

发表评论

kyle
土豪

kyle

注册时间:2016-10-28
最后登录:4小时前
发布
带到手机上看