Java|PageHelper 怎么自作主张帮我分页?

简介: 没有调用 PageHelper.startPage,查询怎么也被自动分页了?

开局上来,我们先看看问题场景的示例代码:

public Page<Xxx> queryXxxList(XxxPageReq req) {

    // some code here

    // 查询一,得到一个结果集,作为查询二的条件
    List<Long> fkIdList = xxxMapper.queryFkIdList(req);
    req.setFkIdList(idList);

    PageHelper.startPage(req.getPageNum(), req.getPageSize());

    // 查询二
    List<Xxx> data = xxxMapper.queryXxxList(req);

    // some code here

}

预期 的逻辑是:查询一不分页,得到一个结果集,作为查询二的条件,查询二分页。

实际 的现象是:查询一被自动添加了 limit,最多只能查询到 10 条数据(示例 req 里的 pageSize 传的 10),导致查询二的查询条件不正确。

分析

初遇到这个问题时,一脸黑人问号,冷静下来后,分析了以下几种可能性,但都一一排除了。

  • 调用当前方法的线程里,已经有其它地方先调用了 PageHelper.startPage(),导致当前方法里的查询一也被分页了;

  • 调用当前方法的线程,上一次调度时设置的分页参数没有被清理;

  • 无厘头的猜想:当前方法是不是在一个大的事务里,而 PageHelper 有什么特殊处理,导致一个事务里的查询都会被分页?

最后通过在 PageInterceptor 里下断点发现了问题所在:

@Override
public Object intercept(Invocation invocation) throws Throwable {
    // some code here 

    //调用方法判断是否需要进行分页,如果不需要,直接返回结果
    if (!dialect.skip(ms, parameter, rowBounds)) {
        // 自动 count 和 分页查询处理
    } else {
        // 跳过 count 和 分页查询处理
    }
    // some code here
}

单步跟进 dialect.skip 方法,关键逻辑在 PageParams.getPage 方法里面:

public Page getPage(Object parameterObject, RowBounds rowBounds) {
    Page page = PageHelper.getLocalPage();
    if (page == null) {
        if (rowBounds != RowBounds.DEFAULT) {
            if (offsetAsPageNum) {
                page = new Page(rowBounds.getOffset(), rowBounds.getLimit(), rowBoundsWithCount);
            } else {
                page = new Page(new int[]{rowBounds.getOffset(), rowBounds.getLimit()}, rowBoundsWithCount);
                //offsetAsPageNum=false的时候,由于PageNum问题,不能使用reasonable,这里会强制为false
                page.setReasonable(false);
            }
        } else if(supportMethodsArguments){
            // 注意这里,我们的查询一进了这个分支
            try {
                page = PageObjectUtil.getPageFromObject(parameterObject, false);
            } catch (Exception e) {
                return null;
            }
        }
        if(page == null){
            return null;
        }
        PageHelper.setLocalPage(page);
    }
    // some code here
}

可以看到,除了显式地提前调用 PageHelper.startPage、传递 rowBounds 参数进行分页外,还有一个 else if(supportMethodsArguments) 的分支,会从传递给查询的参数里尝试读取 pageNumpageSize 字段的值作为分页参数。

随后我查阅了 PageHelper 的官方文档,果然找到了相关的说明:

supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值 false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。

https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

那么,为什么我们项目里的 supportMethodsArguments 会为 true 呢?在代码里没有搜索到,最终在 Apollo 配置中心找到了 pagehelper.supportMethodsArguments = true

破案了。

解决

找到问题就好解决了。因为将 pagehelper.supportMethodsArguments = true 这个配置去掉影响太大不可控,所以此处只是将查询一的参数去掉分页字段即可。

修改后:

public Page<Xxx> queryXxxList(XxxPageReq req) {

    // some code here

    // 查询一,得到一个结果集,作为查询二的条件
    // XxxNoPageReq 与 XxxPageReq 里的字段一样,除了没有 pageSize 和 pageNum
    XxxNoPageReq noPageReq = new XxxNoPageReq();
    BeanUtils.copyProperties(req, noPageReq);
    List<Long> fkIdList = xxxMapper.queryFkIdList(noPageReq);

    // some code here

}

小结

修完老代码里的这个问题,我无奈地笑了。真是前人挖坑,后人被坑啊……

开个玩笑。

这严格来说,其实并不能算一个问题,包括解决它需要走的那些弯路,都只能归咎于对 PageHelper 的用法,以及项目的配置,了解不完全。

码途漫漫,上下求索。


如果读完文章有收获,可以关注我的微信公众号「闷骚的程序员」并🌟设为星标🌟,随时阅读更多内容。

目录
相关文章
|
6月前
|
Web App开发 SQL Java
javaweb实现分页(二)
javaweb实现分页(二)
|
6月前
|
Web App开发 Java 关系型数据库
java中部的分页实现(二)
java中部的分页实现(二)
|
6月前
|
SQL 关系型数据库 MySQL
javaweb中实现分页,持续更新……
javaweb中实现分页,持续更新……
|
6月前
Mybatis+mysql动态分页查询数据案例——分页工具类(Page.java)
Mybatis+mysql动态分页查询数据案例——分页工具类(Page.java)
|
5月前
|
SQL 缓存 Java
Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
|
6月前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(五十八)-java+ selenium自动化测试-分页测试(详细教程)
【5月更文挑战第22天】本文介绍了自动化测试分页的实现方法。首先,文章提出了测试分页时关注的三个关键点:总页数、当前页数和页码导航的可用性。接着,作者分享了一个实用网站([https://www.jq22.com/](https://www.jq22.com/))以找到示例进行实践。在代码部分,展示了使用Java和Selenium进行自动化测试的示例代码,包括获取总页数、遍历所有页面及判断当前页面等操作。最后,简要总结了分页自动化测试的实现过程。
51 1
|
6月前
|
SQL 存储 前端开发
【java】树形结构分页(真分页)
【java】树形结构分页(真分页)
130 1
|
6月前
|
XML 监控 druid
【Java专题_02】springboot+mybatis+pagehelper分页插件+druid数据源详细教程
【Java专题_02】springboot+mybatis+pagehelper分页插件+druid数据源详细教程
104 0
|
4天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
71 38
|
1天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?