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為變量在每個線程中都創建了一個副本,那么每個線程可以訪問自己內部的副本變量。

 然后就看不到任何關聯了,注意上面是有一個注釋,叫 Mybatis - 通用分頁攔截器

那么在哪里攔截了呢?

很熟悉,我們必須要配置的一個配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置分頁插件 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <!-- 設置數據庫類型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六種數據庫-->        
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>
</configuration>

且這里是實現了攔截器的方法:

 *    Copyright 2009-2012 the original author or authors.
package org.apache.ibatis.plugin;

import java.util.Properties;

/**
 * @author Clinton Begin
 */
public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  Object plugin(Object target);

  void setProperties(Properties properties);

}

經過debug,發現攔截器實現是在執行搜索方法selectByExample時候攔截的。

至於怎么通過注解攔截之類的不多說,直接debug分析。

下一步:

這里的autoRuntimeDialect和autoDialect都為false。在processPage方法下打斷點

當然不是在result打斷點,為了方便,是在_processPage方法下打斷點。

這里

是有通過反射獲取參數的,包括方法和類;

這時候page已經獲得一些參數了,但是並沒有獲取到查詢的數據

 

一直往下,直到這里;

注釋的很詳細。這里獲取的是總數。不涉及分頁,跳過;繼續下一步

這里很重要

 這里使用的就是設計模式中的橋接模式;實現parser接口的有mysql,oracle,PostgreSQL等等。

這里實現了拼接;

再反射執行結果,獲取到了值。

至於getPageSql方法在什么時候調用拼接的,暫時沒找到,找到補充


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM