pagehelper常见问题【分页失效】【ThreadLocal污染线程】

🍰 个人主页:不摆烂的小劉
🍞文章有不合理的地方请各位大佬指正。
🍉文章不定期持续更新,如果我的文章对你有帮助➡️ 关注🙏🏻 点赞👍 收藏⭐️

在上一篇文章PageHelper 分页查询【底层代码-图文分析】【原理篇】探讨了PageHelper的底层原理——拦截MyBatis的查询操作,在Sql语句执行前动态地添加分页逻辑,从而实现数据的分页查询。

本篇文章研究一下Pagehelper常出现的问题。
在这里插入图片描述

一、分页参数失效

1. 数据开启分页后查询的集合进行增删

!!!错误代码:

// 开启分页
PageHelper.startPage(1, 10);

// 执行查询
List<Product> products = productDAO.listProducts();

// 在查询结果集上进行增删操作
products.add(newProduct); // 添加新产品
products.remove(newProduct); // 删除产品

新增、删除新的元素,这只是在内存中的List层面进行了操作,并没有改变数据库中的数据分布和分页相关的设置,但会导致当前页数据量不等于PageSize

2. 更新集合生成一个新集合。

分页查询的集合通过lambda得到新的集合
!!!错误代码

PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
List<ProductVO> products = productDAO.listProducts(dto);
// stream处理分页结果集 
List<ProductVO> resultList = products.stream().map(one -> handleProduct(one)).collect(Collectors.toList());
return new PageInfo<>(resultList);

处理后的流收集到一个新的列表中,新的集合resultList并不是 Page 类或其子类的实例,在new PageInfo<>(resultList)中无法设置分页参数(为什么是Page 类或其子类的实例才能设置,请看上一篇(原理篇)的第二章第2、3节

  • 错误代码得到的结果:
    在这里插入图片描述
  • 对比一下正确结果:
    在这里插入图片描述

解决

更新集合问题解决:
PageInfo对象集合对象替换成新集合对象

PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
List<ProductVO> products = productDAO.listProducts(dto);
// stream处理分页结果集,处理后的resultList 集合大小要与原集合products 大小相同
List<ProductVO> resultList = products.stream().map(one -> handleProduct(one)).collect(Collectors.toList());
PageInfo<ProductVO> pageProducts = new PageInfo<>(products);
pageProducts.setList(resultList);
return pageProducts

二、多limit的情况

ThreadLocal 与线程复用导致的 “污染”

  1. Springboot项目中内部使用线程池来处理请求。
  2. 当一个线程完成一个请求的分页查询后,它的 ThreadLocal 中仍然保存着分页参数。
  3. 如果这个线程被线程池复用,去处理查询请求,会导致下一个查询也被添加了分页相关的limit语句

举例说明:线程数为1的线程池,两个查询请求

  1. 请求从#1处进入
  2. 执行到#2,将分页参数存储到当前线程的ThreadLocal中,后面未跟查询
  3. 随后的请求从#3处进入
  4. 线程复用,执行#4处时执行的Sql会被自动添加limit ?,?
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
        1,  // 核心线程数为1
        1,  // 最大线程数为1
        1L, // 线程存活时间1分钟
        TimeUnit.MINUTES,
        new LinkedBlockingQueue<>(5)  // 等待队列大小为5
);

public void listProducts() { //#1
   threadPool.execute(() -> {
        PageHelper.startPage(1, 10); //#2
    });
}
 
public List<ProductVO> listCitys() { //#3
    threadPool.execute(() -> {
        List<City> list = cityDAO.lisCitys(); //#4
        return list;
    });
}

Pagehelper在分页查询后会自动清除ThreadLocal的分页参数。
设置分页参数,但后面未查询,方法直接返回。下次复用线程时,存在线程中的分页参数可能被其他查询消费。

解决

  1. PageMethod.startPage后紧跟查询
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectIf(1);

或者lambda用法

//jdk8 lambda用法
Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());
  1. 使用PageHelper.clearPage()手动清理
PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
List<ProductVO> products = new ArrayList<>();
switch (dto.getBrand()) {
	case "Huawei":
		if ("Huawei".equals(brandToQuery)) {
			products= productDAO.listProducts(dto);
		}
		break;
	case "Apple":
		if ("Apple".equals(brandToQuery)) {
			  products = productDAO.listProducts(dto);
		}
		break;
	default:
		//手动清理
		PageHelper.clearPage()
		break;
}

下一篇文章分析ThreadLocal原理和易错点

三、查询全部数据

分页查询失效,查询了全部数据

1. PageHelper.startPage(1,0)

Pagesize<=0时,不分页,也不会在Sql末尾拼接limit

在这里插入图片描述

2. 错误配置reasonable

设置reasonable=true时,若用户传入页数大于总页数,会修改查询页数,查询最后一页数据;当禁用此参数或设为false,若用户传入页数大于总页数,则返回为空。

3. 没有配置拦截器插件

配置拦截器插件参考:如何使用分页插件

四、其他注意事项

除了上述易错问题,还有其他的注意事项

  1. 紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法会被分页。

  2. 不要在系统中配置多个分页插件,使用Spring时,选择其中一种:mybatis-config.xmlSpring<bean>配置方式

  3. 分页插件不支持带有for update语句的分页

  4. 分页插件不支持嵌套结果映射。嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。

参考:
1.Pagehelper官方文档
2.Mybatis PageHelper分页遇到的坑,莫名其妙的增加了limit ?,?
3.PageHelper失效问题
4.【PageHelper】PageHelper分页失效问题排查
5.浅析PageHelper踩坑:不安全分页导致的问题
6.PageHelper 分页插件使用中的那些“坑”
在这里插入图片描述

🍉文章不定期持续更新,如果我的文章对你有帮助➡️ 关注🙏🏻 点赞👍👍👍 收藏⭐️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值