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‌。

posted on 2025-08-01 21:15  ljbguanli  阅读(25)  评论(0)    收藏  举报