標籤:

Mybatis 插件

上篇文章說道某同事提交了一段Mybatis代碼,利用插件實現分頁查詢的sql組裝。

插件會對四個介面中方法進行攔截,這些介面是

  1. ParameterHandler: 處理參數設置問題
  2. ResultHandler: 結果處理
  3. StatementHandler: 負責連接資料庫,執行sql
  4. 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的有四個類:

  1. RoutingStatementHandler,這是一個封裝類,它不提供具體的實現,只是根據Executor的類型,創建不同的類型StatementHandler。
  2. SimpleStatementHandler,這個類對應於JDBC的Statement對象,用於沒有預編譯參數的SQL的運行。
  3. PreparedStatementHandler 這個用於預編譯參數SQL的運行。
  4. 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 |