Mybatis分頁實現的方法(攔截器+pageHelper)
一、攔截器實現
1.原理
在mybatis 執行過程中攔截執行物件,獲得sql資訊,將分頁資訊新增到sql語句中,然後放行mybatis的執行過程
2.瞭解一點mybatis原始碼
首先我們需要明白要攔截的物件是處理物件(Statement),攔截的時機應該是sql執行之前,
所以我們應該攔截的是: @Intercepts({@Signature(type=StatementHandler.class,method = "prepare",args={Connection.class,Integer.class})})
type 表示需要攔截的類, method 表示需要攔截的方法, args 表示引數
我們這裡攔截主要是(其實這裡攔截到的是而是RoutingStatementHandler,它會通過判斷來決定例項化那個StatementHandler, BaseStatementHandler只是這裡Statement的實現類) BaseStatementHandler ,它實現了Statement。 BaseStatementHandler中通過instantiateStatement 方法來獲取statement物件,instantiateStatement的具體實現在PreparedStatementHandler。
上面的敘述可能會有偏差,原始碼的理解有待提高,這裡看原始碼的主要目的是為了清楚sql的具體資訊在哪裡,比如BaseStatementHandler下的mappedStatement對應的就是mapper 中xml的資訊,BoundSql對應一些sql資訊。
為了在程式碼中獲取到sql的具體資訊我們需要藉助MetaObject這個類,它可以使我們不需要寫反射程式碼獲取到一些必要的資訊。
3.具體程式碼編寫過程
首先,將需要分頁的方法名以”ByPage“結尾 或者什麼標誌標記一下,以確保自己能在後面將這些方法過濾出來
然後,編寫攔截器:
page類:
public class Page { private int totalNumber; private int currentPage; private int totalPage; private int pageNumber = 5; //從第幾條開始 private int dbIndex; //取幾條 private int dbNumber; public void count(){ int totlaPageTemp = this.totalNumber / this.pageNumber; int plus = (this.totalNumber%this.pageNumber)==0?0:1; totlaPageTemp += plus; if( totlaPageTemp <= 0 ){ totlaPageTemp = 1; } this.totalPage = totlaPageTemp; if( this.totalPage < this.currentPage ){ this.currentPage = this.totalPage; } if( this.currentPage < 1 ){ this.currentPage = 1; } this.dbIndex = (this.currentPage-1)*this.pageNumber; this.dbNumber = this.pageNumber; } public int getTotalNumber() { return totalNumber; } public void setTotalNumber(int totalNumber) { this.totalNumber = totalNumber; } public int getCurrentPage() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } public int getTotalPage() { return totalPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public int getPageNumber() { return pageNumber; } public void setPageNumber(int pageNumber) { this.pageNumber = pageNumber; } public int getDbIndex() { return dbIndex; } public void setDbIndex(int dbIndex) { this.dbIndex = dbIndex; } public int getDbNumber() { return dbNumber; } public void setDbNumber(int dbNumber) { this.dbNumber = dbNumber; } @Override public String toString() { return "Page{" + "totalNumber=" + totalNumber + ", currentPage=" + currentPage + ", totalPage=" + totalPage + ", pageNumber=" + pageNumber + ", dbIndex=" + dbIndex + ", dbNumber=" + dbNumber + '}'; } }
/**
* mybatis 分頁攔截器
*/
@Intercepts({@Signature(type=StatementHandler.class,method = "prepare",args={Connection.class,Integer.class})})
public class PageInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler,SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
/*
從RoutingStatementHandler中獲得處理物件PreparedStatementHandler,從這個物件中獲取Mapper中的xml資訊
* */
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
//配置檔案中sql語句的id
String id = mappedStatement.getId();
//判斷是否是需要分頁的方法
if( id.matches(".+ByPage$") ){
//獲得sql的引數資訊
BoundSql boundSql = statementHandler.getBoundSql();
Map<String, Object> parameter = (Map<String, Object>)boundSql.getParameterObject();
Page page = (Page)parameter.get("page");
//原始的sql
String sql = boundSql.getSql();
System.out.println("未改造的sql語句:"+sql);
//組裝page中的資訊,查詢總條數
String countSql = "select count(*) from ("+sql+")a";
System.out.println("查詢總條數sql語句:"+countSql);
Connection connection = (Connection)invocation.getArgs()[0];
PreparedStatement statement = connection.prepareStatement(countSql);
ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(statement);
ResultSet rs = statement.executeQuery();
if( rs.next() ){
int total = rs.getInt(1);
page.setTotalNumber(total);
System.out.println("總條數:"+total);
}else{
System.out.println("未查詢到資料量");
}
page.count();
//改造後的sql語句
String pageSql = sql + " limit "+ page.getDbIndex()+" , "+page.getDbNumber();
System.out.println("改造後的sql語句:"+pageSql);
metaObject.setValue("delegate.boundSql.sql",pageSql);
}
//放行
return invocation.proceed();
}
//被攔截的請求
public Object plugin(Object o) {
return Plugin.wrap(o,this);
}
public void setProperties(Properties properties) {
//這裡可以拿到配置檔案中 plugin 中的propertie的值
}
}
最後,在配置檔案中註冊攔截器
<plugins>
<plugin interceptor="per.ly.interceptor.PageInterceptor" />
</plugins>
4.總結
使用不夠快捷。
二、pageHelper
1、新增依賴
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
2、在mybatis配置檔案中配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
3、實現簡單分頁
PageHelper.startPage(pagenum,pagesize);
4、總結
只能對查詢出的結果進行分頁,可能無法滿足要求。