文章目录
1.问题描述
在多对多查询的时候
如果一个resultMap中有多个collection同时开启懒加载使用了select属性
那么数据就不会被正确返回
1.1.问题背景:
1) 表中数据如下
2) 班级类:
3) SQL语句和resultMap
其中select对应的SQL语句就是根据stuClassId查询老师和学生,此处省略
1.2.查询结果
我们希望得到的结果是:
返回值是List类型,其中有1个StuClass对象,
3个学生保存在这个对象的studentList属性中
2个老师保存在这个对象的teacherList属性中
但查询出来的结果却是:
返回值是List类型,有6个StuClass对象
每一个StuClass对象都保存了3个学生、2个老师的所有信息
这6个StuClass对象的内容完全一致
2.寻找突破口
2.1.修改其中一个collection
如果其中一个collection使用常规的写法,不写select属性
而另外一个collection保持原样
最终返回的结果就会像期望那样
2.2.修改两个collection
两个collection都不使用select属性,结果自然也是正常返回
此处不多赘述
2.3.注释掉两个collection
查阅资料得知,MyBatis在collection中有select时,执行情况是这样的:
当接收数据的collection有select属性时,
进行主查询时会认为这个resultMap中没有这个collection从而跳过
在主查询执行完毕后,再对主查询的每一个结果执行子查询
换言之:如果主查询返回6个对象,那么就会进行6次子查询
按照上述说法,程序主查询时返回了6个内容一致的对象
为了验证这个结果,直接注释掉两个collection以模拟主查询的结果
返回结果如下
说法得到证实
3.情况分析
有了第三个实验的结果,现在就很明朗了
3.1.注释掉两个collection
在resultMap中没有collection标签时,返回的数据不会去重
查询出几条记录,就会返回几个对象
因此返回了6个一模一样的,只有id和name的StuClass对象
3.2.修改其中一个collection
当collection使用传统的写法,没有用到select属性进行子查询时
MyBatis会自动去重,将数据按照我们的期望去封装
程序的执行步骤如下:
执行主查询时,程序搜索到两个collection:
1) 一个没有select属性的collection(List teacherList)
2) 一个有select属性的collection(List studentList)
此时程序会认为resultMap中只有一个teacherList的collection,另一个collection则会被暂时忽略
得到主查询结果后,teacherList(传统写法)的collection接收数据,将数据去重并重新封装,最终精简成为一个对象,作为主查询的结果
随后执行子查询select,主查询有一个对象,因此执行一次子查询,得到最终想要的结果
3.3.修改两个collection
两个collection都不使用select属性,结果自然也是正常返回
此处不多赘述
4.解决方案
如果两个都必须开启延迟加载,目前来说暂时没有什么好的办法。
可用的解决方案如下:
1) 都不开启延迟加载,不要使用select属性
2) resultMap中只写一个collection,另一个collection的数据在service中进行二次查询,这个可以考虑用一个Util统一实现
部分的核心代码如下,仅供参考: