Mybatis查询:结果集的顺序引起的数据缺失和重复的坑

上一篇文章遇到一个问题:Mybatis多表关联内连接和左连接结果不一致

详情请看文章链接: https://blog.csdn.net/qq_18259401/article/details/80025869

最后问了好些人都不知道原因,网上也搜不到,无奈花了三个多小时debug看源码才发现问题,而且这个问题还是源码引起的,如下:

1.使用结果错误的内连接查询语句dubug情况如下:

在源码dubug到org.apache.ibatis.executor.resultset.NestedResultSetHandler 类中如下方法:

Mybatis查询:结果集的顺序引起的数据缺失和重复的坑_第1张图片

这个方法的作用大概就是:根据结果集中查询到的数据装配到返回的实体类属性中

在调用该方法时参数ResultSet rs中已经存在查询结果如下:

Mybatis查询:结果集的顺序引起的数据缺失和重复的坑_第2张图片


因不好截图,我这里文字说明ResultSet中结果(主要是结果集的顺序):

如上图,结果中有10条数据,顺序依次如下(相应列出对应的userID和gradeID):

rs[0] --->49          userID=1          gradeID=2      

rs[1] --->51          userID=3          gradeID=1    

rs[2] --->52          userID=4          gradeID=2    

rs[3] --->53          userID=5          gradeID=2    

rs[4] --->54          userID=6          gradeID=2    

rs[5] --->55           userID=7         gradeID=2    

rs[6] --->56          userID=8          gradeID=2    

rs[7] --->50          userID=2          gradeID=1    

rs[8] --->50          userID=2          gradeID=2    

rs[9] --->51          userID=3          gradeID=2    


上面的handleRowValues方法在执行处理ResultSet结果集的顺序如下:

Mybatis查询:结果集的顺序引起的数据缺失和重复的坑_第3张图片

83行先将rs中下一个userID给rowKey,我这里第一条数据userID是1,所以rowKey=1

84行去objectCache中查询rowKey=1的user是否存在,此时objectCache和rowValue都还是没有数据的,因此85行条件不成立,所以不会运行87行(87行是将user装配到resultHandler中,我们最后的结果就是从resulthandler中取的)

89行,会根据rowKey将userID=1的用户查出来放到objectCache中,且将查出的user对象赋值给rowValue

第一次循环结束,rowKey=1,rowValue=user1 ,objectCache中也只有一个user1,resultHandler中没有数据


第二次执行while循环,83行中将rs中第二个userID给rowKey,此时rowKey=3

84行去objectCache中查询rowKey=3的user没有查到且rowValue此时是user1不为null,

因此85行条件成立会执行87行,87行将rowValue中user1装配到resultHandler中

再执行89行,根据rowKey=3查询将结果user3放到objectCache中,且之后rowValue=user3

第二次循环结束,rowKey=3,rowValue=user3 ,objectCache中有user1和user3,resultHandler中有user1


第三次执行while循环,83行中将rs中第3个userID给rowKey,此时rowKey=4

84行去objectCache中查询rowKey=4的user没有查到且rowValue此时是user3不为null,

因此85行条件成立会执行87行,87行将rowValue中user3装配到resultHandler中

再执行89行,根据rowKey=4查询将结果user4放到objectCache中,且之后rowValue=user4

第三次循环结束,rowKey=4,rowValue=user4 ,objectCache中有user1和user3和user4,resultHandler中有user1和user3


......依次循环到第七次结束后:rowKey=8,rowValue=user8,

objectCahce中数据有:user1,user3,user4,user5,user6,user7,user8

resulthandler中数据有:user1,user3,user4,user5,user6,user7


第八次循环,83行中将rs中第8个userID给rowKey,此时rowKey=2,

84行去objectCache中查询rowKey=2的user没有查到且rowValue此时是user8不为null,

因此85行条件成立会执行87行,87行将rowValue中user8装配到resultHandler中

再执行89行,根据rowKey=2查询将结果user2放到objectCache中,且之后rowValue=user2

第八次循环结束,rowKey=2,rowValue=user2 ,

objectCache中有:user1,user3,user4,user5,user6,user7,user8,user2

resultHandler中有:user1,user3,user4,user5,user6,user7,user8


第九次循环,83行中将rs中第8个userID给rowKey,此时rowKey=2,

84行去objectCache中查询rowKey=2的user发现user2存在

因此85行条件不成立不会执行87行,

再执行89行,根据rowKey=2查询发现objectCache中存在user2,则user2的List属性添加新的grade对象(详情请看上篇文章),之后rowValue=user2

第九次循环结束,rowKey=2,rowValue=user2 ,

objectCache中有:user1,user3,user4,user5,user6,user7,user8,user2(两个grade)

resultHandler中有:user1,user3,user4,user5,user6,user7,user8


第十次循环,83行中将rs中第8个userID给rowKey,此时rowKey=3,

84行去objectCache中查询rowKey=3的user发现user3存在

因此85行条件不成立不会执行87行,

再执行89行,根据rowKey=3查询发现objectCache中存在user3,则user3的List属性添加新的grade对象(详情请看上篇文章),之后rowValue=user3

第十次循环结束,rowKey=3,rowValue=user3 ,

objectCache中有:user1,user3(两个grade),user4,user5,user6,user7,user8,user2(两个grade)

resultHandler中有:user1,user3(两个grade),user4,user5,user6,user7,user8


此时循环已经结束,执行循环外第91行,发现rowValue=user3不为空,则将rowValue中user3装配到resultHandler中,

此时装配结束后:rowKey=3,rowValue=user3 ,

objectCache中有:user1,user3(两个grade),user4,user5,user6,user7,user8,user2(两个grade)

resultHandler中有:user1,user3(两个grade),user4,user5,user6,user7,user8,user3(两个grade)

而最终的结果是根据resultHandler中结果返回。


所以最后的我查询获取到的结果错误如下:

UserID: 1  UserName: 李四  UserPW: lisi  UserAge: 24  size: 1
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查
UserID: 3  UserName: 王武  UserPW: wangwu  UserAge: 23  size: 2
------ GradeID: 1   GradeIden: 管理员   GrdeJurs: 增删改查
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查

UserID: 4  UserName: 赵柳  UserPW: zhaoliu  UserAge: 24  size: 1
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查
UserID: 5  UserName: 陈琦  UserPW: chenqi  UserAge: 23  size: 1
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查
UserID: 6  UserName: 张八  UserPW: zhangba  UserAge: 23  size: 1
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查
UserID: 7  UserName: 曹久  UserPW: caojiu  UserAge: 22  size: 1
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查
UserID: 8  UserName: 刘师  UserPW: liushi  UserAge: 25  size: 1
------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查
UserID: 3  UserName: 王武  UserPW: wangwu  UserAge: 23  size: 2
------ GradeID: 1   GradeIden: 管理员   GrdeJurs: 增删改查

------ GradeID: 2   GradeIden: 普通用户   GrdeJurs: 查


刚开始查询后的ResultSet类型中的rs对象值是正确的:1345678223,而装配结束后正确结果应该是:13456782,但最后的查询结果却是错误的:13456783

而当我使用左连接查询时,ResultSet中值为:1223345678,该代码装配时在循环执行第四次时判断user3在objectCache中不存在,而rowValue此时是user2,所以85行条件成立会执行87行,会将user2装配进resultHandler中,所以结果正确为:12345678

原因总结:上面结果错误的原因主要是因为最后一个user2在需要装配到resultHandler时没有装配进去,因为user3在之前已经装配进objectCache中,所以在判断下一个userID=3时objectCache已有,则不会执行if内的语句,即不会将rowValue=user2装配进resultHandler中。而无论如何循环结束后在循环外最后一条数据user3都会被装配进resultHandler中,所以user3在结果中是重复的

技术总结:所以在使用mybatis查询时查询结果的顺序也会影响最终的顺序,尽量避免存在如我上面的数据形式,在查询时去重或者尽量使用排序,在主外表选择时要注意主表的数据是否存在如下数据顺序:ABCDDB(结果为ABCB,缺少D且B重复);ABCDB(结果为ABCB,缺少D且B重复);ABCDDBEF(结果为ABCEF,缺少D);如果存在,则在sql语句中需要对该结果顺序进行处理,不然会出错。

个人觉得该代码处理确实算是个坑,所以以后还得注意!!!







你可能感兴趣的:(Mybatis查询:结果集的顺序引起的数据缺失和重复的坑)