项目中使用PageHelper进行分页查询的时候,发现分页有问题:每页显示的数量与传入的limit的参数不一致。
细细查看之后,发现是由于查询SQL是一对多查询,才会出现这样的问题。在网上查找了一些解决方案后,最终解决了,下面是整个查找问题、解决问题的记录。
背景介绍
已知用户表User,角色表Role,用户角色关联表user_role。其中,User和user_role表是一对多的关系。要求查询用户信息并进行分页,其中用户信息中需包含该用户的所有角色信息。
问题查找
问题页面如下图:
1)每一页10条/页,第一页应该显示10条数据,但是表格中只有6条;
2)总条数24,也比User表在数据库中的条数(14条)多。
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:
<!--这个是查询具体信息的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条记录:
这样一看,原因就显而易见了:
因为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"进行查询结果映射