Mybatis 插件
上篇文章說道某同事提交了一段Mybatis代碼,利用插件實現分頁查詢的sql組裝。
插件會對四個介面中方法進行攔截,這些介面是
- ParameterHandler: 處理參數設置問題
- ResultHandler: 結果處理
- StatementHandler: 負責連接資料庫,執行sql
- Executor: 對上述過程的調度組織.
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})public class PaginationInterceptor implements Interceptor { ……}public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties);}
攔截器類需要實現Interceptor介面,還需要有註解@Intercepts標識具體哪個類的那個方法需要被攔截。
分頁查詢需要在intercept(Invocation invocation)調用之前,算出要查詢的頁具體在哪幾行數據,並且重寫sql,然後再進行後面的流程。
在MyBatis實現了statementHandler的有四個類:
- RoutingStatementHandler,這是一個封裝類,它不提供具體的實現,只是根據Executor的類型,創建不同的類型StatementHandler。
- SimpleStatementHandler,這個類對應於JDBC的Statement對象,用於沒有預編譯參數的SQL的運行。
- PreparedStatementHandler 這個用於預編譯參數SQL的運行。
- CallableStatementHandler 它將實存儲過程的調度。
那具體實現類是哪個呢?追蹤源碼不難發現是RoutingStatementHandler
// Configuration.java public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
// RoutingStatementHandler.javaprivate final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); }}
可以到看到,RoutingStatementHandler只是一個Wrapper,裡面的delegate才是最終的實現類。那麼具體是哪一個呢?
我們知道,任何框架的初始化都是解析配置文件的過程,然後把元數據載入到內存中。內存中的MappedStatement 對應的就是xml文件中的statement,其默認類型是PreparedStatement
<insert id="save" statementType="STATEMENT"><!-- STATEMENT,PREPARED 或CALLABLE -->
我們還看到會調用interceptorChain.pluginAll,返回的是代理對象(JDK動態帶來)。所以我們調用的是代理對象。
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); }}
在Spring創建org.mybatis.spring.SqlSessionFactoryBean的時候,調用addInterceptor(),這裡使用的責任鏈模式。
最後在同時的代碼中看到一個小技巧:如果一共有N條記錄,每頁有M條記錄,那麼頁數為
(N-1)/M + 1,其實這有漏洞,當N=0的時候,不能成立。
參考資料:
Mybatis插件原理
Mybatis源碼分析(二)--Sqlsession的執行流程
推薦閱讀:
※Mybatis5:resultMap與resultType總結
※Mybatis9:Mybatis與Hibernate區別
※mybatis和hibernate區別大不大?
※MyBatis中緩存
※Mybatis6:延遲載入
TAG:MyBatis |