PageHelper工作原理
數據分頁功能是我們軟件系統中必備的功能,在持久層使用mybatis的情況下,pageHelper來實現后臺分頁則是我們常用的一個選擇,所以本文專門類介紹下。
PageHelper原理
相關依賴
1.添加plugin
??要使用PageHelper首先在mybatis的全局配置文件中配置。如下:
2.加載過程
??我們通過如下幾行代碼來演示過程
加載配置文件我們從這行代碼開始
new SqlSessionFactoryBuilder().build(inputStream);public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}
小結:通過SqlSessionFactory對象的獲取,我們加載了全局配置文件及映射文件同時還將配置的攔截器添加到了攔截器鏈中。
3.PageHelper定義的攔截信息
??我們來看下PageHelper的源代碼的頭部定義
PageHelper中已經定義了該攔截器攔截的方法是什么。
4.Executor
??接下來我們需要分析下SqlSession的實例化過程中Executor發生了什么。我們需要從這行代碼開始跟蹤
增強Executor
??到此我們明白了,Executor對象其實被我們生存的代理類增強了。invoke的代碼為
該方法中的內容我們后面再分析。Executor的分析我們到此,接下來看下PageHelper實現分頁的具體過程。
5.分頁過程
??接下來我們通過代碼跟蹤來看下具體的分頁流程,我們需要分別從兩行代碼開始:
5.1 startPage
PageHelper.startPage(1, 5);
5.2selectList方法
session.selectList("com.bobo.UserMapper.query"); public <E> List<E> selectList(String statement) {return this.selectList(statement, null); }public <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT); }
我們需要回到invoke方法中繼續看
進入sqlUtil.processPage(invocation);方法
/*** Mybatis攔截器方法** @param invocation 攔截器入參* @return 返回執行結果* @throws Throwable 拋出異常*/ private Object _processPage(Invocation invocation) throws Throwable {final Object[] args = invocation.getArgs();Page page = null;//支持方法參數時,會先嘗試獲取Pageif (supportMethodsArguments) {// 從線程本地變量中獲取Page信息,就是我們剛剛設置的page = getPage(args);}//分頁信息RowBounds rowBounds = (RowBounds) args[2];//支持方法參數時,如果page == null就說明沒有分頁條件,不需要分頁查詢if ((supportMethodsArguments && page == null)//當不支持分頁參數時,判斷LocalPage和RowBounds判斷是否需要分頁|| (!supportMethodsArguments && SqlUtil.getLocalPage() == null && rowBounds == RowBounds.DEFAULT)) {return invocation.proceed();} else {//不支持分頁參數時,page==null,這里需要獲取if (!supportMethodsArguments && page == null) {page = getPage(args);}// 進入查看return doProcessPage(invocation, page, args);} } /*** Mybatis攔截器方法** @param invocation 攔截器入參* @return 返回執行結果* @throws Throwable 拋出異常*/private Page doProcessPage(Invocation invocation, Page page, Object[] args) throws Throwable {//保存RowBounds狀態RowBounds rowBounds = (RowBounds) args[2];//獲取原始的msMappedStatement ms = (MappedStatement) args[0];//判斷并處理為PageSqlSourceif (!isPageSqlSource(ms)) {processMappedStatement(ms);}//設置當前的parser,后面每次使用前都會set,ThreadLocal的值不會產生不良影響((PageSqlSource)ms.getSqlSource()).setParser(parser);try {//忽略RowBounds-否則會進行Mybatis自帶的內存分頁args[2] = RowBounds.DEFAULT;//如果只進行排序 或 pageSizeZero的判斷if (isQueryOnly(page)) {return doQueryOnly(page, invocation);}//簡單的通過total的值來判斷是否進行count查詢if (page.isCount()) {page.setCountSignal(Boolean.TRUE);//替換MSargs[0] = msCountMap.get(ms.getId());//查詢總數Object result = invocation.proceed();//還原msargs[0] = ms;//設置總數page.setTotal((Integer) ((List) result).get(0));if (page.getTotal() == 0) {return page;}} else {page.setTotal(-1l);}//pageSize>0的時候執行分頁查詢,pageSize<=0的時候不執行相當于可能只返回了一個countif (page.getPageSize() > 0 &&((rowBounds == RowBounds.DEFAULT && page.getPageNum() > 0)|| rowBounds != RowBounds.DEFAULT)) {//將參數中的MappedStatement替換為新的qspage.setCountSignal(null);// 重點是查看該方法BoundSql boundSql = ms.getBoundSql(args[1]);args[1] = parser.setPageParameter(ms, args[1], boundSql, page);page.setCountSignal(Boolean.FALSE);//執行分頁查詢Object result = invocation.proceed();//得到處理結果page.addAll((List) result);}} finally {((PageSqlSource)ms.getSqlSource()).removeParser();}//返回結果return page;} 進入 BoundSql boundSql = ms.getBoundSql(args[1])方法跟蹤到PageStaticSqlSource類中的@Override protected BoundSql getPageBoundSql(Object parameterObject) {String tempSql = sql;String orderBy = PageHelper.getOrderBy();if (orderBy != null) {tempSql = OrderByParser.converToOrderBySql(sql, orderBy);}tempSql = localParser.get().getPageSql(tempSql);return new BoundSql(configuration, tempSql, localParser.get().getPageParameterMapping(configuration, original.getBoundSql(parameterObject)), parameterObject); } @Override protected BoundSql getPageBoundSql(Object parameterObject) {String tempSql = sql;String orderBy = PageHelper.getOrderBy();if (orderBy != null) {tempSql = OrderByParser.converToOrderBySql(sql, orderBy);}tempSql = localParser.get().getPageSql(tempSql);return new BoundSql(configuration, tempSql, localParser.get().getPageParameterMapping(configuration, original.getBoundSql(parameterObject)), parameterObject); }
也可以看Oracle的分頁實現
至此我們發現PageHelper分頁的實現原來是在我們執行SQL語句之前動態的將SQL語句拼接了分頁的語句,從而實現了從數據庫中分頁獲取的過程。
總結
以上是生活随笔為你收集整理的PageHelper工作原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python的convert_pytho
- 下一篇: springboot---整合redis