Mybatis分页查询当前页数据条数大于实际返回的数据条数
1.神奇Bug描述
使用Mybatis进行分页查询时,使用到了如下sql:
select DISTINCT id,name,engineering_role from role;
数据库查询出来一共9条数据,使用MybatisPlus进行查询时,
Page对象返回的记录条数也是9条,但是Record字段却只有8条数据。
Debug过程中发现返回的VO对象里面多了一个SQL语句没有的字段engineeringRoleList,但是engineeringRoleList字段确是有值的。
2.还原案发现场
多的engineeringRoleList是因为使用了自定义的resultMap映射,动态进行了字段添加,案例如下:
<resultMap id="PersonResultMap" type="PersonVo"><id property="id" column="id"/><result property="name" column="name"/><collection property="engineeringRoleList" ofType="string"><result column="engineering_role"/></collection></resultMap>
上述SQL语句查询结果,刚好有2条数据除了engineering_role 的值不同,其他字段的值是相同的。
3.DK大师分析
这个问题是由于MyBatis的标签在分页查询时导致的数据折叠现象。
当使用进行一对多映射时,数据库返回的多条关联记录会被合并到主对象的集合属性中,导致实际返回的记录数少于分页查询的总数。
主要原因包括
1.分页插件(如PageHelper)基于SQL结果条数拦截,但会将相同主表记录合并
2.联表查询时,子表的多条记录会使主表数据重复,经折叠后数量减少
解决方案建议
改用嵌套select查询方式(注意N+1问题)
<collection property="engineeringRoleList" ofType="string" select="selectRoles" column="id"/>
业务层二次处理:先获取分页的主表数据,再批量查询关联数据
使用子查询避免联表:
SELECT r.id, r.name, (SELECT GROUP_CONCAT(engineering_role) FROM role WHERE id = r.id) AS engineeringRoleList
FROM role r
如果不需要分页,可移除改为单独查询
对于当前案例,建议检查是否有重复的id值导致合并,或考虑使用DISTINCT配合GROUP_CONCAT函数重构SQL。