一,概述
MyBatis的分頁使用的關(guān)鍵字就是limit,插件分頁的原理就是在sql語句中拼接limit關(guān)鍵字,進(jìn)行數(shù)據(jù)的分頁查詢,所以pageHelper也不例外,底層處理的就是使用的代理對象拼接的Sql,實(shí)現(xiàn)的分頁。其實(shí)思想都離不開原始的分頁的sql語句,雖然這樣的分頁插件好用,但是建議初學(xué)者最好不要使用,如果要使用,也要吃透底層的原理才可以加深理解, 否則的話,可能只會使用不能明白其中的原理。
pageHelper使用的是ThreadLocal本地線程變量的特性來進(jìn)行存儲分頁的的部分?jǐn)?shù)據(jù),使用到分頁查詢的參數(shù)組裝,而且在使用一次后,就會被remove,但是這個(gè)是必須的,一個(gè)是防止分頁數(shù)據(jù)被其他sql誤用,一個(gè)是防止內(nèi)存泄露。使用的是mybatis的實(shí)現(xiàn)Interceptor接口,使用jdk動態(tài)代理的模式,增強(qiáng)Executor對象,實(shí)現(xiàn)的代理對象,組裝count和limit分頁數(shù)據(jù)的查詢的sql語句,返回一個(gè)page對象,page對象其實(shí)就是繼承了list對象,所以返回的List對象是父類,適應(yīng)多態(tài)。
二,原理分析設(shè)置分頁數(shù)據(jù)PageHelper.startPage(重寫startPage)
封裝分頁的pageNum pageSize以及orderFiled的一些數(shù)據(jù)
public static Page startPage(int pageNum, int pageSize, String orderBy) {Page page = startPage(pageNum, pageSize);page.setOrderBy(orderBy);return page;public static Page startPage(int pageNum, int pageSize, Boolean count, Boolean reasonable, Boolean pageSizeZero) {Page page = new Page(pageNum, pageSize, count);page.setReasonable(reasonable);page.setPageSizeZero(pageSizeZero);Page oldPage = getLocalPage();if (oldPage != null && oldPage.isOrderByOnly()) {page.setOrderBy(oldPage.getOrderBy());//設(shè)置分頁的數(shù)據(jù)存放在ThreadLocal里面,setLocalPage(page);return page;
以上是可以看出,分頁的數(shù)據(jù)是和ThreadLocal綁定的,也是將數(shù)據(jù)存放在ThreadLocal里面,提供給后續(xù)分頁的時(shí)候使用
清除ThreadLocal分頁數(shù)據(jù)
以上圖你可以看出,清除數(shù)據(jù)的是在com.github.pagehelper.PageInterceptor#intercept方法中的finally中執(zhí)行的,概述也說過,其實(shí)執(zhí)行的原理就是利用的mybatis的intercept的進(jìn)行的對象的代理
Pageinterceptor代理的源頭
@Intercepts注解就是標(biāo)注需要攔截代理的對象,type就是對象為Executor,方法是query,參數(shù)為兩個(gè)重載的方法,PageInterceptor實(shí)現(xiàn)Interceptor并實(shí)現(xiàn)intercept和plugin方法,進(jìn)行數(shù)據(jù)代理的處理,setProperties方法其實(shí)是可以設(shè)置配置參數(shù)來處理,實(shí)現(xiàn)接口原因是因?yàn)閙ybatis使用的jdk動態(tài)代理,必須是實(shí)現(xiàn)接口的才可以創(chuàng)建代理對象,下面就intercept方法進(jìn)行分析分頁的處理邏輯
intercept方法
this.count進(jìn)行查詢統(tǒng)計(jì)總數(shù)
就是使用原sql語句進(jìn)行count查詢總數(shù)
com.github.pagehelper.util.ExecutorUtil#pageQuery
dialect.getPageSql就是拼接sql語句中的limit
看到這里,可能就可以明白了,這個(gè)分頁到底是怎么做的 了,其實(shí)就是sql拼接了limit,總上圖就可以看出,規(guī)歸根結(jié)底就是代理了Excutor對象,然后執(zhí)行count和拼接limit的sql語句,實(shí)現(xiàn)的插件的分頁
小結(jié)
以上可以看出來,其實(shí)就是一個(gè)Excutor代理對象的一系列處理,簡單而言,pageHelper的分頁邏輯處理,可以就這么理解,其實(shí)就是生成代理對象并代理增強(qiáng),從ThreadLocal中獲取到分頁的參數(shù),拼接sql和查詢總數(shù)的sql并返回一個(gè)page對象
三,Excutor邏輯補(bǔ)充
首先寫分頁插件的話,需要知道m(xù)ybatis的原理,執(zhí)行Sql的是四大對象:Executor,StatementHandler,ParameterHandler,ResultSetHandler。
一般都會獲取SqlSession,獲取途徑一般都是SqlSessionFactory .openSession
Executor的創(chuàng)建
增強(qiáng)對象
從上面可以看出,其實(shí)Executor創(chuàng)建的過程就是執(zhí)行了攔截器inteceptor中的攔截器鏈