Mybatis實現分頁功能
由於我之前有一篇部落格寫到實現分頁功能,簡單的分頁功能,但是如果我們有多個頁面,就需要每一個頁面都寫一個這個麻煩的程式碼,顯得非常的愚笨。於是出現了Mybatis分頁,有點類似於Spring的AOP(這裡不講解)。
首先,我們是想一個特定查詢的語句執行的時候需要進行攔截,執行分頁的功能。
一般的步驟是寫一個攔截類,然後註冊。
我們需要一個類,這裡叫做PageInterceptor
package com.interceptor; import main.entity.Page; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import java.sql.ResultSet; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.Map; import java.util.Properties; /** * @program: Tradingplatform * @description:分頁攔截器 * @author: Robert_Wang * @create: 2018-09-16 19:46 **/ //攔截的類+方法(防止過載,需加上引數) @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})}) public class PageInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //獲取攔截目標 StatementHandler statementHandler = (StatementHandler)invocation.getTarget(); //元物件, 由於我們想要獲得一個 protected ResultMap,所以用反射來實現 //將元物件方便我們訪問屬性 MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY); //按照OGNL表示式 訪問屬性 //獲取與xml所對應的關鍵資訊 MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement"); //配置檔案中sql的id, 即 queryListByPage String id = mappedStatement.getId(); //正則表示式 .+ 至少出現一個字元 $是結束符 if(id.matches(".+ByPage$")){ BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); String countSql = "select count(*) from (" + sql + ")a"; Connection connection = (Connection)invocation.getArgs()[0]; PreparedStatement countStatement = connection.prepareStatement(countSql); ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler"); parameterHandler.setParameters(countStatement); ResultSet rs = countStatement.executeQuery(); Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject(); Page page = (Page)parameter.get("page"); if(rs.next()){ page.setTotalNumber(rs.getInt(1)); } String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber(); //修改sql語句 metaObject.setValue("delegate.boundSql.sql",pageSql); } return invocation.proceed(); } @Override public Object plugin(Object o) { //返回代理類 return Plugin.wrap(o,this); } @Override public void setProperties(Properties properties) { } }
plugin方法是返回一個代理過的類,引數是代理的物件。
setProperties是用於設定引數。是在進行註冊的時候設定的引數。
interceptor是攔截的過程,也是本次的核心。
我們先明確目的:在查詢語句的PreparedStatement前進行攔截,那麼我們就要
獲得xml中的sql語句, 執行相關的操作(查詢總條數、計算多少個頁面、等)
然後我們再細分這個過程慢慢解決。
攔截的類前面加一個註解,表示攔截該類時簽有型別、方法的類,引數就是區分過載函式。
首先獲取攔截目標 -->獲取sql語句-->>進行sql相關的操作
再進行細分;
首先獲取攔截目標:StatementHandler
獲取sql語句之前需要在xml獲取,於是需要MappedStatement這個類,這個類是需要元物件MetaObject來獲取的,
而元物件是需要StatementHandler。
於是就關聯起來了,其實用到的就是StatementHandler MetaObject StatementHandler最主要的三個類。
然後就是SQL操作了:先判斷攔截的SQL是不是我們想要的SQL,如果是,再查詢條數,設定引數值即可。
再進行細分: 查詢條數:拼接SQL語句-->獲取連線-->預編譯-->引數設定。
其他的細枝末節就這樣分了,下面不再詳細分了。