mybatisplus+PageHelper 一对多查询 分页问题

项目中使用PageHelper进行分页查询的时候,发现分页有问题:每页显示的数量与传入的limit的参数不一致。
细细查看之后,发现是由于查询SQL是一对多查询,才会出现这样的问题。在网上查找了一些解决方案后,最终解决了,下面是整个查找问题、解决问题的记录。

背景介绍

已知用户表User,角色表Role,用户角色关联表user_role。其中,User和user_role表是一对多的关系。要求查询用户信息并进行分页,其中用户信息中需包含该用户的所有角色信息。

问题查找

问题页面如下图:
1)每一页10条/页,第一页应该显示10条数据,但是表格中只有6条;
2)总条数24,也比User表在数据库中的条数(14条)多。
mybatisplus+PageHelper 一对多查询 分页问题_第1张图片
SQL文件是这样的:

	<resultMap id="userMap" type="xxx.entity.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        ...
        <collection property="roles" ofType="xxx.entity.Role">
            <id column="role_id" property="id"/>
            <result column="role_name" property="roleName"/>
        </collection>
	</resultMap>
    
    <select id="listByPage" resultMap="userMap" parameterType="xxx.ReqParam">
        select a.*, b.role_id, c.role_name
        from user a
        join user_role b on (a.id = b.user_id)
        join role c on (b.role_id = c.id)
        <trim prefix="WHERE" prefixOverrides="AND|OR">
            <if test="searchKey != null and searchKey != '' and searchValue != null and searchValue != ''">
                AND a.${searchKey} like concat('%',#{searchValue},'%')
            </if>
        </trim>
        order by a.create_date
    </select>

查看后台日志后发现,PageHelper进行分页时是先执行一个查询count的SQL,得出总条数,后又执行具体的SQL进行信息查询。

 <!--这个是查询count的SQL-->
 SELECT COUNT(0) 
 FROM user a 
 JOIN user_role b ON (a.id = b.user_id) 
 JOIN role c ON (b.role_id = c.id) 

把这条查询count的SQL拿到数据库执行的结果,显示count是24:
mybatisplus+PageHelper 一对多查询 分页问题_第2张图片

 <!--这个是查询具体信息的SQL-->
SELECT a.*, b.role_id, c.role_name 
FROM user a 
JOIN user_role b ON (a.id = b.user_id) 
JOIN role c ON (b.role_id = c.id) 
ORDER BY a.create_date LIMIT 10 

把这条查询具体信息的SQL拿到数据库执行,查出来是10条记录:
mybatisplus+PageHelper 一对多查询 分页问题_第3张图片
这样一看,原因就显而易见了:
因为User和user_role表是一对多的关系,所以在关联查询时,虽然是查出来了10条记录,但是这10条记录中,user有重复的(因为一个user有多个角色)。
而这些记录在封装成User对象返回前端时,会按User进行逐个封装,这样就会出现数量减少的情况。

问题解决

进过多方查阅,看了这几位的经验分享后:
https://www.cnblogs.com/bt2882/p/11088738.html.
https://blog.csdn.net/qq_35885488/article/details/82527577.
https://blog.csdn.net/KokJuis/article/details/73564989.
我采用了他们的做法,终于可以啦
以下是修改之后的代码:

<resultMap id="userMap" type="xxx.entity.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="nick_name" property="nickName"/>
        <result column="real_name" property="realName"/>
        <result column="avatar" property="avatar"/>
        <result column="sex" property="sex"/>
        <result column="phone" property="phone"/>
        <result column="email" property="email"/>
        <result column="state" property="state"/>
        <result column="create_date" property="createDate"/>
        <result column="update_date" property="updateDate"/>
        <result column="company_id" property="companyId"/>
        <result column="company_name" property="companyName"/>
        <result column="office_id" property="officeId"/>
        <result column="office_name" property="officeName"/>

        <collection property="roles" ofType="xxx.entity.Role"
                    javaType="java.util.List" select="getRoleInfo" column="id">
        </collection>
    </resultMap>

    <resultMap id="roles" type="xxx.entity.Role">
        <id column="role_id" property="id"/>
        <result column="role_name" property="roleName"/>
    </resultMap>

<select id="getRoleInfo" parameterType="java.lang.String" resultMap="roles">
        select b.role_id, c.role_name
        from user_role b
        join role c on (b.role_id = c.id)
        where b.user_id = #{id}
</select>
    
<select id="listByPage" resultMap="userMap" parameterType="xxx.ReqParam">
        select a.*
        from user a
        <trim prefix="WHERE" prefixOverrides="AND|OR">
            <if test="searchKey != null and searchKey != '' and searchValue != null and searchValue != ''">
                AND a.${searchKey} like concat('%',#{searchValue},'%')
            </if>
        </trim>
        order by a.create_date
</select>

将主要的查询语句剥离出来,需要关联表查询的信息,通过配置相应的collection,并设置select指向相关的查询sql:
1:先将User主表查询SQL1拆出来
2:在这个SQL1的resultMap=“userMap” 中配置collection ,指向查询这个用户对应的角色的SQL2(select=“getRoleInfo”)
3:在查询这个用户对应的角色的SQL2中,通过resultMap="roles"进行查询结果映射

你可能感兴趣的:(MyBatis)