thinkphp3.2.3(5以下)的addAll返回值问题

背景(欲快速解决问题,直接看最后一部分即可)

我们都知道mysql支持一次插入多条数据,如下:
以用户表user为例,表结构自增主键id、账号username、密码password。

insert into user(username,password) values ('fpf','123456'),('pig','258310');

TP借助PDO连接mysql,完成上述操作。

除了增删改查,PDO还有一些函数,如lastInsertId(返回最后插入行的ID)、PDOStatement::rowCount(返回上一个由对应的 PDOStatement 对象执行DELETE、 INSERT、或 UPDATE 语句受影响的行数)等。参考1参考2

问题

对于tp3.2.3的addAll方法,发现其返回值与官方文档说的返回最后一个id不一样,而是返回第一个id。比如用addAll方法向user表中一次插入1、2、3共三条数据,它返回的是第1条数据的id,而非官方定义的第3条数据的id。

$m=M('User');
$data=[['username'=>'fpf','password'=>'123456'],['username'=>'pig','password'=>'258310']];
$pk=$m->addAll($data);
dump($pk);

运行该带码,dump的数据是5,而在数据库中
id=5对应数据[‘username’=>‘fpf’,‘password’=>‘123456’];
id=6对应数据[‘username’=>‘pig’,‘password’=>‘258310’]。
显然tp官方文档定义相悖。

原因

追查TP3.2.3的ORM实现机制发现,其addAll方法的返回值直接用pdo的lastInsertId方法获得,在tp目录ThinkPHP\Library\Think\Db下的Driver.class.php文件的execute方法中,具体实现方法如下:

在这里插入图片描述问题就在pdo的lastInsertId方法上,它是调用mysql api中的mysql_insert_id函数返回的值。
mysql_insert_id函数和SELECT LAST_INSERT_ID()作用类似,但又有一些区别,在下面代码情景中作用相同,具体不同看官方文档,直接在数据库管理工具(如navicat)上执行下面mysql脚本就会发现问题。

insert into user(username,password) values ('fpf','123456'),('pig','258310');
SELECT LAST_INSERT_ID()

在这里插入图片描述实际结果:
在这里插入图片描述
从两个图片结果可以看到,一次插入两条,通过SELECT LAST_INSERT_ID()查到的id=24,而实际最后一个id=25,这是什么原因呢?具体原因可以看看这篇官方对该语句的解释,这是mysql的一个处理策略,主要是因为可以轻松地实现其他服务器复制相同的INSERT语句。当然,在一次插入一条时,这个现象时不存在的,那么mysql_insert_id返回的结果也就正确了。
在这里插入图片描述

mysql官方文档介绍mysql_insert_id函数解释如下:详细参考官方文档

官方文档主要说了mysql_insert_id函数返回的是储存在有AUTO_INCREMENT约束的字段的值,如果表中的字段不使用AUTO_INCREMENT约束或者使用自己生成的唯一值插入,那么该函数不会返回你所存储的值,而是返回NULL或0。因此,在没有使用AUTO_INCREMENT约束的表中,或者ID是自己生成的唯一ID,lastInsertId函数返回的都是0。

也可以参考这两篇文章,有实际例子说明,更好,重点看看第一篇。文章1文章2

解决方案

利用pdo的rowCount方法,具体代码如下:
原理:lastID=firstID+rowCount-1;
在这里插入图片描述具这样修改TP3.2.3的addAll方法就可以正常放回最后一行的id了。
样另外TP5及以上一次插入多条的方法是insertAll,它返回的是添加成功的条数,当然,这些方法的前提都是插入执行成功。将tp3.2.3目录ThinkPHP\Library\Think\Db下的Driver.class.php文件的execute方法改成下图即可:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章