<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
PageHelper是一個非常好用的外掛,以至於很想知道它底層是怎麼實現的。至於MyBatis外掛概念原理網上有很多,我不太喜歡去寫一些概念性的東西,我比較喜歡自己動手實現的那種,話不多說,我們開幹
搭建一個SpringBoot+MyBatis+MySql專案
package com.example.demo.plugin; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class MyPagePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由於邏輯關係,只會進入一次 if (args.length == 4) { //4 個引數時 boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 個引數時 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } == 其實在這之上的程式碼都是拷貝PageHelper原始碼來的,下面才是重頭戲,上面都是獲取一些必要的引數== /** * 下面4行程式碼暫時只需要知道是用來傳引數的就行,分頁不是需要 * 二個引數嘛 一個是當前頁,一個是數量 */ Page page = ThreadLocalUtil.getPage(); Map<String,Object> params = new HashMap<>(); params.put("first_key",page.getPageNum()); params.put("second_key",page.getPageSize()); /** * 重點:獲取資料庫記錄總數 */ //統計總數 Long count = MyExecutorUtil.executeAutoCount(executor,ms,parameter,boundSql,rowBounds,resultHandler); /** * 重點:分頁查詢 */ List<Object> objects = MyExecutorUtil.pageQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey, params); return objects; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }
1:獲取記錄總數:
MyExecutorUtil.executeAutoCount(executor,ms,parameter,boundSql,rowBounds,resultHandler);
2:分頁查詢:
MyExecutorUtil.pageQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey,params);
1:MyBatis原始碼底層會封裝好我們在組態檔中寫的每一條SQL語句,封裝到一個SqlSource物件中去,在我們執行SQL的時候,會獲取到這條SQL,然後封裝到BoundSql這個物件中,所以在這裡,我們既然能拿到BoundSql,那麼也就意味著我們能拿到我們需要執行的那條Sql了
2:獲取到了我們的sql之後,怎麼查詢總記錄數呢??
其實很簡單,改Sql語句不就好了,Pagehelper底層也是這麼做的,但是PageHelper底層比我這個版本的複雜太多了,但是我們無非就是將原先的SQL轉換成 -> SELECT COUNT(0) FROM TABLE,就這樣,但是我這個人比較懶,而且昨天看這個原始碼實現頭疼,所以在這裡直接寫死了,但是問題不大哈
3:改好了Sql之後是不是就完成了呢??
當然不是,改好了SQL當然是要去執行它了,如果就這樣執行,還是會執行原先的SQL,但是你要知道這個有個很關鍵的東西,就是MappedStatement的id,如果不改這個id的話,即使你的Sql能夠執行成功,那麼返回的記錄總數是個NULL,這裡我想可能是因為ResultSetHandle的關係,因為這個id對應的還是我們之前的sql,也就是select * from student,那麼必然有一個resultType的屬性,也就是實體類對映,但是我們現在的sql是select count(0) from student,那麼就對應不上了,也就是資料庫查詢出來的列與實體類對應不上,所以我們需要改變這個id。
//改變MapperStatement id的方法
public static MappedStatement newCountMappedStatement(MappedStatement ms, String newMsId) { MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), newMsId, ms.getSqlSource(), ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) { StringBuilder keyProperties = new StringBuilder(); for (String keyProperty : ms.getKeyProperties()) { keyProperties.append(keyProperty).append(","); } keyProperties.delete(keyProperties.length() - 1, keyProperties.length()); builder.keyProperty(keyProperties.toString()); } builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); //count查詢返回值int List<ResultMap> resultMaps = new ArrayList<ResultMap>(); ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId(), Long.class, EMPTY_RESULTMAPPING).build(); resultMaps.add(resultMap); builder.resultMaps(resultMaps); builder.resultSetType(ms.getResultSetType()); builder.cache(ms.getCache()); builder.flushCacheRequired(ms.isFlushCacheRequired()); builder.useCache(ms.isUseCache()); return builder.build(); }
4:MyBatis的Sql是封裝到BoundSql中去的,而原先的BoundSql中的Sql是我們組態檔中的,所以我們需要將select count(0) from student這條Sql語句封裝到一個新的BoundSql中去
public static Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //改變MapperStatement中的ID屬性 String countMsId = countMs.getId() + "_COUNT"; countMs = newCountMappedStatement(countMs,countMsId); //建立 count 查詢的快取 key CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql); //呼叫獲取count的sql String countSql = "select count(0) from student"; //重新封裝BoundSql物件 BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter); //執行 count 查詢 List<Long> countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql); Long count = countResultList.get(0); return count; }
5:最後呼叫executor.query方法就可以得到我們的記錄總數了
1:我這裡使用的是ThreadLocal,
原始碼:
就下面這5個類,其它的就是將SpringBoot+MyBatis+MySql日常設定就行了,然後再Controller記得傳遞一下引數
package com.example.demo.plugin; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @MapperScan({"com.example.demo.mapper"}) public class MapperConfig { @Bean public MyPagePlugin myPagePlugin() { return new MyPagePlugin(); } }
package com.example.demo.plugin; import com.example.demo.entity.Student; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.*; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; public class MyExecutorUtil { private static final List<ResultMapping> EMPTY_RESULTMAPPING = new ArrayList<ResultMapping>(0); public static <E> List<E> pageQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, CacheKey cacheKey,Map<String,Object> params) throws SQLException { executor.clearLocalCache(); //生成分頁的快取 key CacheKey pageKey = cacheKey; //處理引數物件 if(params.size() < 0 || params.size() > 2) { System.out.println("引數錯誤"); } //獲取sql String pageSql = getPageSql(params.size(),boundSql); List<ParameterMapping> mappingList = new ArrayList<>(); mappingList.add(new ParameterMapping.Builder(ms.getConfiguration(), "first_key", Integer.class).build()); mappingList.add(new ParameterMapping.Builder(ms.getConfiguration(), "second_key", Integer.class).build()); //範例化新的BoundSql物件 BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, mappingList, params); //執行分頁查詢 return executor.query(ms, params, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql); } public static Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //改變MapperStatement中的ID屬性 String countMsId = countMs.getId() + "_COUNT"; countMs = newCountMappedStatement(countMs,countMsId); //建立 count 查詢的快取 key CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql); //呼叫獲取count的sql String countSql = "select count(0) from student"; //重新封裝BoundSql物件 BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter); //執行 count 查詢 List<Long> countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql); Long count = countResultList.get(0); return count; } public static MappedStatement newCountMappedStatement(MappedStatement ms, String newMsId) { MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), newMsId, ms.getSqlSource(), ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) { StringBuilder keyProperties = new StringBuilder(); for (String keyProperty : ms.getKeyProperties()) { keyProperties.append(keyProperty).append(","); } keyProperties.delete(keyProperties.length() - 1, keyProperties.length()); builder.keyProperty(keyProperties.toString()); } builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); //count查詢返回值int List<ResultMap> resultMaps = new ArrayList<ResultMap>(); ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId(), Long.class, EMPTY_RESULTMAPPING).build(); resultMaps.add(resultMap); builder.resultMaps(resultMaps); builder.resultSetType(ms.getResultSetType()); builder.cache(ms.getCache()); builder.flushCacheRequired(ms.isFlushCacheRequired()); builder.useCache(ms.isUseCache()); return builder.build(); } private static String getPageSql(int paramsLength,BoundSql boundSql){ StringBuilder str = new StringBuilder(); str.append(boundSql.getSql()); if(paramsLength == 1) { str.append(" LIMIT ?"); }else { str.append(" LIMIT ?,? "); } return str.toString(); } }
package com.example.demo.plugin; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class MyPagePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由於邏輯關係,只會進入一次 if (args.length == 4) { //4 個引數時 boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 個引數時 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } Page page = ThreadLocalUtil.getPage(); Map<String,Object> params = new HashMap<>(); params.put("first_key",page.getPageNum()); params.put("second_key",page.getPageSize()); //統計總數 Long count = MyExecutorUtil.executeAutoCount(executor,ms,parameter,boundSql,rowBounds,resultHandler); System.out.println("count = " + count); List<Object> objects = MyExecutorUtil.pageQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey, params); return objects; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }
package com.example.demo.plugin; public class Page { private Integer pageNum; private Integer pageSize; public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } }
package com.example.demo.plugin; public class ThreadLocalUtil { public static ThreadLocal<Page> PAGE_INFO = new ThreadLocal<>(); public static void startPage(int pageNum,int pageSize) { Page page = new Page(); page.setPageNum(pageNum); page.setPageSize(pageSize); PAGE_INFO.set(page); } public static Page getPage() { return PAGE_INFO.get(); } }
@RestController public class StudnetController { @Autowired private StudentMapper studentMapper; @GetMapping("/getData") public Object getData() { ThreadLocalUtil.startPage(0,2); return studentMapper.getData(); } }
以上就是Mybatis分頁外掛PageHelper手寫實現範例的詳細內容,更多關於Mybatis分頁外掛PageHelper的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45