pagehelper分页原理浅析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pagehelper分页原理浅析相关的知识,希望对你有一定的参考价值。

参考技术A 先看一下pagehelper的简单用法:

<dependency>

    <groupId>com.github.pagehelper</groupId>

    <artifactId>pagehelper-spring-boot-starter</artifactId>

    <version>1.2.5</version> 我使用的版本

</dependency> 依赖

首先要注册pagehelper:

@Bean

public PageHelpercreatePaeHelper()

PageHelper page=new PageHelper();

  return page;



使用:

PageHelper.startPage(page, limit);

List list =customerSubMapper.findCustAndSubInfo(status, custid, subid, name, d1, d2);

PageInfo pageInfo =new PageInfo<>(list);

在看到PageInfo的字段(总页数、第几页、是否第一页、是否最后一页等等)和PageInfo的用法时,我第一时间想到的是pagehelper不过是先把分页需要的参数放到page里,再进行全查,再把所有数据丢到PageInfo里进行分页,也没什么厉害的。不过当我debugger时,发现查询到的list数据只有一页的条数,我之前的想法是不对的。百度一下,网上说的是使用了mybatis的拦截器。

查询发现有个PageInterceptor的实现类,进去看看intercepter方法(拦截方法)

往sql语句里加了orderby从句,再看下重置方法:

是个抽象方法,再看看子类的是怎么实现的

因为我使用的是mysql,所以这里以mysql为例,实现类是MySqlDialect,可以看到分页是在这里加上的。

有一个疑问是为什么PageHelper.startPage方法要写在查询语句的前面

重载方法最全的一个,看下getLocalPage()方法,

可以看到pagehelper使用线程容器保存了分页的数据,在使用拦截器的时候查询线程容器里有没有分页数据,有就添加分页从句,没有就什么也不用做。

PageHelper实现分页原理

com.github.pagehelper.PageHelper是一个开源的分页源码工具; 第一次看的时候不知道分页原理是什么?

看代码:

    @Override
    public ResultUtil selLogList(Integer page, Integer limit,UserSearch search) {
        PageHelper.startPage(page, limit);
        TbLogExample example=new TbLogExample();
        //设置按创建时间降序排序
        example.setOrderByClause("id DESC");
        Criteria criteria = example.createCriteria();
        
        if(search.getOperation()!=null&&!"".equals(search.getOperation())){
            criteria.andOperationLike("%"+search.getOperation()+"%");
        }
        
        if(search.getCreateTimeStart()!=null&&!"".equals(search.getCreateTimeStart())){
            criteria.andCreateTimeGreaterThanOrEqualTo(MyUtil.getDateByString(search.getCreateTimeStart()));
        }
        if(search.getCreateTimeEnd()!=null&&!"".equals(search.getCreateTimeEnd())){
            criteria.andCreateTimeLessThanOrEqualTo(MyUtil.getDateByString(search.getCreateTimeEnd()));
        }
        
        List<TbLog> logs = tbLogMapper.selectByExample(example);
        PageInfo<TbLog> pageInfo = new PageInfo<TbLog>(logs);
        ResultUtil resultUtil = new ResultUtil();
        resultUtil.setCode(0);
        resultUtil.setCount(pageInfo.getTotal());
        resultUtil.setData(pageInfo.getList());
        return resultUtil;
    }

在dao层调用selectByExample之前只使用了

PageHelper.startPage(page, limit);进行分页。

在这两者之间似乎并没有什么重要的关联,没有相互引用。

再看mybatis的xml文件。

  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.hfcx.pojo.TbLogExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from tb_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>

一般来说limit是写在order by之后的,可是xml文件中并没有limit。

这时我们看一下PageHelper.startPage(page, limit);的源码;

    /**
     * 开始分页
     *
     * @param pageNum      页码
     * @param pageSize     每页显示数量
     * @param count        是否进行count查询
     * @param reasonable   分页合理化,null时用默认配置
     * @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        //当已经执行过orderBy的时候
        Page<E> oldPage = SqlUtil.getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        SqlUtil.setLocalPage(page);
        return page;
    }

在这段代码中方法内第一行是写入参数,第二个和第三个都是为null,不去考虑。

重点在于SqlUtil.getLocalPage();和SqlUtil.setLocalPage(page);方法。

这里是使用进行分页的,继续进去看源代码;

    /**
     * 获取Page参数
     *
     * @return
     */
    public static <T> Page<T> getLocalPage() {
        return LOCAL_PAGE.get();
    }

    public static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

这里有一个LOCAL_PAGE;

技术图片

这里用到了一个ThreadLocal;

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。

ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

 

以上是关于pagehelper分页原理浅析的主要内容,如果未能解决你的问题,请参考以下文章

PageHelper实现分页原理

PageHelper分页原理源码分析

PageHelper分页原理源码分析

Mybatis插件原理和PageHelper结合实战分页插件

MyBatis使用pagehelper进行分页

MyBatis使用pagehelper进行分页