看下调用的方法 this.getGeneratedKeysInternal()
protected ResultSet getGeneratedKeysInternal() throws SQLException { // 获取影响的行数 int numKeys = this.getUpdateCount(); return this.getGeneratedKeysInternal(numKeys); }这里有个重要知识点了,首先获取本次批量插入的影响行数,然后再执行具体的获取id操作。
getGeneratedKeysInternal方法
protected synchronized ResultSet getGeneratedKeysInternal(int numKeys) throws SQLException { Field[] fields = new Field[]{new Field("", "GENERATED_KEY", -5, 17)}; fields[0].setConnection(this.connection); fields[0].setUseOldNameMetadata(true); ArrayList rowSet = new ArrayList(); long beginAt = this.getLastInsertID(); // 按照受影响的范围+递增步长 for(int i = 0; i < numKeys; ++i) { if (beginAt > 0L) { // 值塞进去 row[0] = StringUtils.getBytes(Long.toString(beginAt)); } beginAt += (long)this.connection.getAutoIncrementIncrement(); } }迭代影响的行数,然后依次获取id。
所以批量insert是正确可以返回的。
但是批量insertOrUpdate就有问题了,批量insertOrUpdate的影响行数不是插入的数据行数,可能是0,1,2这样就导致了自增id有问题了。
比如插入3条数据,2条会update,1条会insert,这时候updateCount就是5,generateid就会5个了,mybatis然后取前3个塞到数据里,显然是错的。
以上是原理分析,如果想了解更详细的实验结果,可以看下
总结 批量insert <insert useGeneratedKeys="true" keyProperty="id"> insert into Author (username, password, email, bio) values <foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio}) </foreach> </insert>来自官网的例子,mapper中不能指定@Param参数,否则会有问题
批量insertOrUpdate不能依赖useGeneratedKey返回主键。
关注公众号【方丈的寺院】,第一时间收到文章的更新,与方丈一起开始技术修行之路
https://blog.csdn.net/slvher/article/details/42298355
https://blog.csdn.net/qq_27680317/article/details/81118070#%EF%BC%883%EF%BC%89%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5
https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-usagenotes-last-insert-id.html
https://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.html
https://dev.mysql.com/doc/refman/5.5/en/example-auto-increment.html