pageHelper分页失效解决方案

pageHelper分页失效解决方案前言 pageHelper 是一款优秀的 Mybatis 分页插件 在项目中可以非常便利的使用 使开发效率得到很大的提升 但不支持一对多结果映射的分页查询 所以在平时的使用时 对于一对多分页会出现分页错误 这篇文章主要对 pageHelper 分页错误进行重现以及提出解决方案 mybatis 进行一对多查询时 映射文件 mapper xml 中的 sql 语句中使用的左连接 pageHelper 会自动对这条左连接 sql 语句进行 selectcount 0 的处理 并把结果作为分页结构的记录总数 然后自动将 lim

前言

      pageHelper是一款优秀的Mybatis分页插件,在项目中可以非常便利的使用,使开发效率得到很大的提升,但不支持一对多结果映射的分页查询,所以在平时的使用时,对于一对多分页会出现分页错误,这篇文章主要对pageHelper分页错误进行重现以及提出解决方案。

分析

       mybatis进行一对多查询时,映射文件(mapper.xml)中的sql语句中使用的左连接,pageHelper会自动对这条左连接sql语句进行select count(0)的处理,并把结果作为分页结构的记录总数,然后自动将limit拼接到sql语句末尾进行分页,由于左连接查询时,连接条件on条件不唯一(即一对多)时,结果会产生笛卡尔积,所以经过pagehelper插件分页得到的记录总数和分页结果并不是预期的结果。

数据准备

共两个表:user、address,用户id与收货地址表中userId对应。

用户表【user】:11条数据

pageHelper分页失效解决方案

收货地址信息表【address】:4条数据

pageHelper分页失效解决方案

数据结构

public class UserDto { public int id; public String name; List 
addressList; }

预期结果

要求对数据进行分页(每页5条),获得用户信息,每个用户信息带出对应收货信息, 用户id为2和3的用户各有两条收货地址信息,其余没有。期望结果如下

{ "code": 200, "message": "success", "data": { "pageNum": 1, "pageSize": 5, "pages": 3, "size": 5, "total": 11, "data": [ { "id": 1, "name": "张三", "addressList": [] }, { "id": 2, "name": "李四", "addressList": [ { "id": 1, "address": "陕西省宝鸡市", "userId": 2 }, { "id": 2, "address": "陕西省延安市", "userId": 2 } ] }, { "id": 3, "name": "王五", "addressList": [ { "id": 3, "address": "陕西省西安市", "userId": 3 }, { "id": 4, "address": "陕西省汉中市", "userId": 3 } ] }, { "id": 4, "name": "钱六", "addressList": [] }, { "id": 5, "name": "刘七", "addressList": [] } ] } }

问题重现

mybatis映射文件

 
   
    
    
    
     
     
    
   

然后我们使用pageHelper进行分页,并输出日志

SELECT count(0) FROM user a LEFT JOIN address b ON a.id = b.userId Preparing: SELECT a.*,b.address,b.userId FROM user a LEFT JOIN address b on a.id=b.userId LIMIT ? Parameters: 5(Integer) Total: 5

日志分析

第1行:进行数据总数的查询,作为数据的总条数total

第2-4行:进行分页结果的查询,查询出5条数据

从日志中可以看出

1. pageHelper插件拼接后的sql语句就不会输出正确的结果,更不会输出符合期望的结果

2. pageHelper插件分两步查询,第一步查询出记录总数,第二步查询出分页结果

解决方案

方案一

思路:先分页查询出user表数据,然后在serviec服务层根据用户id查询对应的收货地址信息,并关联用户信息与收货信息。

service文件

public List 
  
    findAll(){ List 
   
     userList=userMapper.findUser(); userList.forEach((item)-> { item.setAddressList(userMapper.findByUserId(item.id)); }); return userList; } 
    
  

mybatis映射文件

 

方案二

思路:使用mybatis的嵌套子查询

 
   
    
    
    
     
     
     
    
   
    
   

与嵌套映射结构的resultMap格式基本一致,一对多查询采用的依旧是collection,区别在于collection中多了select与column属性,select用于加载子查询映射语句的id,它会从column属性指定的列中检索数据,作为参数传递给目标select语句即子查询。

缺点:这种方式虽然可以解决pagehelper一对多分页的问题,但在大型数据表与数据集上性能表现不佳,即产生’1+N’问题。

输出以下sql日志:首先通过主查询语句获得主表的数据总量作为分页的total,第二步通过limit获得前5条分页数据(就是‘1’),第三步将第二步获得结果作为参数通过子查询获得地址表的信息(就是‘N’)

Preparing: SELECT count(0) FROM user Parameters: Total: 1 Preparing: select * from user LIMIT ? Parameters: 5(Integer) Preparing: select a.* from address a where a.userId=? Parameters: 1(Integer) Total: 0 Preparing: select a.* from address a where a.userId=? Parameters: 2(Integer) Total: 2 Preparing: select a.* from address a where a.userId=? Parameters: 3(Integer) Total: 2 Preparing: select a.* from address a where a.userId=? Parameters: 4(Integer) Total: 0 Preparing: select a.* from address a where a.userId=? Parameters: 5(Integer) Total: 0

方案三

思路:弃用pageHelper插件,自定义分页查询,先对主表(user)进行分页,并把分页结果作为虚拟表与副表(address)进行左连接查询

 
   
    
    
    
     
     
    
   

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228359.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午7:12
下一篇 2026年3月16日 下午7:13


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号