Mybatis自定義輕量級分頁組件(易集成,易拓展)
其實github有一個叫做PageHelper的開源分頁組件,我也用過,封裝的還可以,只是感覺他的量級偏重,其實很多引數,都是我們開發中不需要的引數,而且它的獲取分頁資訊方式是通過構造方法,不是很優雅,所以我在查閱完他的原始碼后,結合自己的需求,實作了一個輕量級,便攜式的分頁組件SmallPage,
SmallPage
輕量級分頁組件,基于入參型別
核心依賴
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
功能描述
- 自動攔截進行分頁
- 基于Dao層方法入參型別
- 自動count總條數
核心類 - PageInterceptorPlugin
- 使用方式,只需將Dao查詢方法的入參Bean繼承于PageWrapperBean,設定Page物件則可以實作分頁,分頁完成后的資訊也保存在Page物件中
- 入參型別可以parameterType 定義,也可以@Param("")傳入,多個入參時,只要其中一個入參型別為PageWrapperBean的上轉型別,就可以實作分頁
- 入參型別不為PageWrapperBean的上轉型別時,直接推進攔截鏈
實作方式
現在java的持久層框架大多基于Mybatis的框架,所以實作分頁的思路,也就攔截待執行的sql進行追加Limt限定,來實作分頁,
/**
* @author machenike
*/
@Intercepts(@Signature(type = StatementHandler.class,method = "prepare",args={Connection.class,Integer.class}))
public class PageInterceptorPlugin implements Interceptor {
private static final String META_OBJECT_KEY_SQL_ID = "delegate.mappedStatement.id";
private static final String META_OBJECT_KEY_BOUND_SQL = "delegate.boundSql.sql";
private static final Logger logger = LoggerFactory.getLogger(PageInterceptorPlugin.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
//獲取命令handler取得
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
//獲取入參handler
ParameterHandler parameterHandler = statementHandler.getParameterHandler();
//獲取引數物件
Object parameterObject = parameterHandler.getParameterObject();
PageBean pageBean = getPageBean(parameterObject);
ThreadLocal threadLocal = new ThreadLocal();
if(pageBean!=null) {
logger.debug("parameter type match,start intercept");
//獲取meta物件取得
MetaObject metaObject = MetaObject.forObject(
statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
new DefaultReflectorFactory());
//獲取當前sqlId
String sqlId = (String)metaObject.getValue(META_OBJECT_KEY_SQL_ID);
//原來應該執行的sql吧
String sql = statementHandler.getBoundSql().getSql();
logger.debug("-originSql:" + sql);
Connection connection = (Connection) invocation.getArgs()[0];
String countSql = bulidCountSql(sql);
logger.debug("-countSql:" + countSql);
//渲染引數
PreparedStatement preparedStatement = connection.prepareStatement(countSql);
//條件交給mybatis
parameterHandler.setParameters(preparedStatement);
//讓mybatis執行這個sql
ResultSet resultSet = preparedStatement.executeQuery();
int count = 0;
if (resultSet.next()) {
count = resultSet.getInt(1);
}
resultSet.close();
preparedStatement.close();
//limit 1 ,10 十條資料 總共可能有100 count 要的是 后面的100
pageBean.setTotal(count);
//拼接分頁陳述句(limit) 并且修改mysql本該執行的陳述句
String pageSql = buildPageSql(pageBean, sql);
logger.debug("-pageSql:" + pageSql);
//重新系結分頁sql
metaObject.setValue(META_OBJECT_KEY_BOUND_SQL, pageSql);
}
//推進攔截器呼叫鏈
return invocation.proceed();
}
/**
* 入參物件中取得分頁物件
* @param o
* @return
*/
private PageBean getPageBean(Object o){
PageWrapperBean pageWrapperBean = null;
PageBean pageBean = null;
//型別判斷
//入參為Map 上轉型別
if(o instanceof Map){
//迭代Map
Map<String,Object> paramMap = (Map<String, Object>) o;
Set<String> keySet = paramMap.keySet();
for(String key:keySet){
Object valueObject = paramMap.get(key);
if(valueObject instanceof PageWrapperBean){
pageWrapperBean = (PageWrapperBean) valueObject;
break;
}
}
//入參為 PageWrapperBean 上轉型別
} else if(o instanceof PageWrapperBean){
pageWrapperBean = (PageWrapperBean) o;
}
pageBean = pageWrapperBean.getPage();
return pageBean;
}
/**
* 構建記錄條數countSql
* @param originSql
* @return
*/
private String bulidCountSql(String originSql){
//優化一下就是講select 到from 之間的字串替換為 count(1) 即可,這里僅為了方便
String countSql = "select count(1) from ("+originSql+") a";
return countSql;
}
/**
* 構建分頁sql
* @param pageBean
* @param originSql
* @return
*/
private String buildPageSql(PageBean pageBean,String originSql){
//拼接分頁陳述句(limit) 并且修改mysql本該執行的陳述句
String pageSql = originSql+" limit "+pageBean.getStart()+","+pageBean.getLimit();
return pageSql;
}
}
所有的分頁都是PageInterceptorPlugin,因為傳入的分頁資訊都會封裝到pageWrapperBean 當中的page物件中,同時page物件也是前端傳輸分頁資訊,本來就請求的當前執行緒所持有,完全不用考慮執行緒安全,不需要采用PageHelper中ThreadLocal去保存分頁資訊,
攔截StatementHandler中從StatementHandler取出原始的查詢sql,通過ParameterHandler 獲取分頁引數,
然后將原始的sql通過上述代碼中buildPageSql方法進行構建分頁sql
/**
* 自動運算相關數值
*/
public void autoCount(){
if(limit!=null&&limit>0&&total!=null){
pageCount = total/limit+1;
}
if(limit!=null&&limit>0&¤tPage>=1) {
start = (currentPage - 1) * limit;
}
}
我將這個方法放入了,page物件的set方法中,一旦有分頁引數變化,分頁資訊就會有所變化,
satrt的起始的索引的運算公式就是
index = (currentPage - 1) * limit
當前頁-1*limit
獲取記錄總條數的sql通上述的bulidCountSql去構建,
“select count(1) from (”+originSql+") a";
最后使用preparedStatement這個命令物件完成查詢,將回傳的分頁引數,設定到page物件中,
最后在外部使用getPage()方法既可以拿到所有的分頁資訊,
使用方式
入參物件使用類繼承的方式
public class TestEntity extends PageWrapperBean {
private Integer id;
private String text;
}
分頁使用方式
TestEntity testEntity = new TestEntity();
PageBean pageBean = new PageBean();
pageBean.setCurrentPage(1);
pageBean.setLimit(10);
//設定分頁引數
testEntity.setPage(pageBean);
//開始查詢
testMapper.select(testEntity);
//取得分頁資訊
PageBean pageInfo = testEntity.getPage();
入參物件使用@Param 這種Map形式的
List<TestEntity> query(@Param("page")PageWrapperBean pageWrapperBean);
分頁使用方式
PageBean pageBean = new PageBean();
pageBean.setCurrentPage(1);
pageBean.setLimit(10);
//設定分頁引數
PageWrapperBean pageWrapperBean = new PageWrapperBean(pageBean);
//開始查詢
testMapper.query(pageWrapperBean);
//取得分頁資訊
PageBean pageInfo = pageWrapperBean.getPage();
}
最后附上原始碼地址,后續還繼續優化更新
https://github.com/DavidLei08/SmallPage
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/231101.html
標籤:其他
上一篇:java執行緒之Lock鎖,三個執行緒搶票加上lock鎖后變成三個執行緒排隊買票
下一篇:【白帽子學習筆記】CTF實踐
