mybatis插件實踐

Mybatis是當今最普遍使用的數據持久化框架,這點拒絕反駁。

Mybatis調用鏈:

SqlSessionInterceptor.invoke()
    DefaultSqlSessionFactory.openSession(executorType): DefaultSqlSession
    ->Configuration.newExecutor(): sqlSession   //interceptorChain.pluginAll(excutor); 
    DefaultSqlSession.selectList(String statement, Object parameter, RowBounds rowBounds)
    ->CachingExecutor.query(); =>SimpleExecutor.query()  //queryFromDatabase
          ->Configuration.newStatementHandler(): StatementHandler
          ->new RoutingStatementHandler();
              ->configuration.newParameterHandler()   //interceptorChain.pluginAll(parameterHandler);
              ->configuration.newResultSetHandler();  //interceptorChain.pluginAll(resultSetHandler);
          ->interceptorChain.pluginAll(statementHandler);  //*****註冊插件(共四類)、生成代理類
              ->Plugin.wrap(target, this); ->Proxy.newProxyInstance(cl, interfaces, new Plugin(target, interceptor, signatureMap));
          ->RoutingStatementHandler.prepare(connection, timeout);
          ->RoutingStatementHandler.parameterize(stmt)

可見,mybatis可以再四個地方使用插件:Executor、ParameterHandler、ResultHandler、StatementHandler。大部分情況下我們都會實現Executor的插件,比如分頁、改變sql參數等。自定義一個插件主要三步:

1、繼承 org.apache.ibatis.plugin.Interceptor,並實現方法 Object intercept(Invocation invocation),mybatis_3.5之前還要實現Object plugin(Object target) ;

2、聲明攔截的類型,如:@Intercepts({ 
    @Signature(type=Executor.class, method="update", args={ MappedStatement.class, Object.class }),
    @Signature(type=Executor.class, method="query", args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}

3、加入spring容器中,特別要注意它處於InterceptorChain鏈中的位置,這樣會直接影響執行效果,因爲mybatis的攔截器執行順序與聲明的位置相反。

最後特別要說明一個網上流行的分頁組件 github.PageHelper,這哥們做的特別噁心,將自己放在最後一個,即第一個執行,並且硬性改變了executor的執行方法,使得後面攔截器可能會無法執行,具體看他的實現:

@Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            .....
            if (args.length == 4) {
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                cacheKey = (CacheKey) args[4];
                boundSql = (BoundSql) args[5];
            }
            checkDialectExists();

            List resultList;
            if (!dialect.skip(ms, parameter, rowBounds)) {
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
                resultList = ExecutorUtil.pageQuery(dialect, executor,
                        ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            ....
        }
    }

從這裏看出,它破壞了chain的執行順序,並沒有調用 invocation.proceed(),而是直接調用了留個參數的query方法。

說完了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章