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方法在什么時候調用拼接的,暫時沒找到,找到補充