关于PageHelper分页失效的源码分析

本文通过源码分析,揭示了PageHelper分页在第二次执行时失效的原因,主要涉及LocalThread的清理以及com.github.pagehelper.PageInterceptor和com.github.pagehelper.Dialect中的关键方法dialect.skip和dialect.afterAll,这两个方法导致后续SQL不再进行分页。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先说结论,第2次分页失效的原因是第一次查询后将LocalThread清除了。
环境介绍

pagehelper版本

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.13</version>
</dependency>
<!--对应的pagehelper实际如下-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.1.11</version>
</dependency>

测试代码

    @Test
    public void page() {
        PageHelper.startPage(2, 2);
        List<PersonPO> personPOS = personMapper.selectAll();
        //下面这条会查出所有数据,即不会分页。即只有紧跟着PageHelper后面的语句才会分页
        List<PersonPO> personPOS1 = personMapper.selectAll();
        PageInfo<PersonPO> pageInfo = new PageInfo<>(personPOS);
        log.info("pageInfo:{}", pageInfo);
    }

源码简要分析
主要类com.github.pagehelper.PageInterceptor

package com.github.pagehelper;
/**
* 删掉依赖及注释
*/
public class PageInterceptor implements Interceptor {
    //这里略去其他的代码

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            //...
            //调用方法判断是否需要进行分页,如果不需要,直接返回结果
            if (!dialect.skip(ms, parameter, rowBounds)) {
                //走这里,则分页
            } else {
                //走这里,则不会分页
            }
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if(dialect != null){
            //这里最终会调到LOCAL_PAGE.remove()方法,后文有细说
                dialect.afterAll();
            }
        }
    }
    //这里略去其他的代码
}

这里有两个关键方法
dialect.skip(ms, parameter, rowBounds)和dialect.afterAll(),都是定义在com.github.pagehelper.Dialect下

package com.github.pagehelper;

/**
 * 删除注释和import
 */
public interface Dialect {
    /**
     * 跳过 count 和 分页查询
     *
     * @param ms              MappedStatement
     * @param parameterObject 方法参数
     * @param rowBounds       分页参数
     * @return true 跳过,返回默认查询结果,false 执行分页查询
     */
    boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds);
    //略过一些其他代码
    /**
     * 完成所有任务后
     */
    void afterAll();
}

先看dialect.skip(ms, parameter, rowBounds)方法,PageHelper中的实现如下

    @Override
    public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        if (ms.getId().endsWith(MSUtils.COUNT)) {
            throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");
        }
        //继续跟进pageParams.getPage(parameterObject, rowBounds),可以看到调用的是LOCAL_PAGE.get(),而LOCAL_PAGE就是ThreadLocal<Page>。
        //ps:在调用PageHelper.startPage()类似方法时,进行ThreadLocal的set;
        //这里先给结论,第一次page不为null,第二次page为null。
        //因此dialect.skip(ms, parameter, rowBounds)第一次调用时,返回false,第二次返回true。
        //所以第一次进分页,第二次不会进分页
        //第二次不进分页,那么就意味着,必然有地方调用了LOCAL_PAGE.remove(),也就是com.github.pagehelper.page.PageMethod的clearPage()方法;还记得dialect.afterAll()这个方法么,这里就是调用LOCAL_PAGE.remove()的入口。
        Page page = pageParams.getPage(parameterObject, rowBounds);
        if (page == null) {
            return true;
        } else {
            //设置默认的 count 列
            if (StringUtil.isEmpty(page.getCountColumn())) {
                page.setCountColumn(pageParams.getCountColumn());
            }
            autoDialect.initDelegateDialect(ms);
            return false;
        }
    }

再看dialect.afterAll(),PageHelper中的实现如下

    @Override
    public void afterAll() {
        //这个方法即使不分页也会被执行,所以要判断 null
        AbstractHelperDialect delegate = autoDialect.getDelegate();
        if (delegate != null) {
            delegate.afterAll();
            autoDialect.clearDelegate();
        }
        //这里清除ThreadLocal
        clearPage();
    }

所以,紧跟着PageHelper.startPage(2, 2)后面的sql,会执行分页,而后的sql,都不会分页。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值