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方法。

说完了。

你可能感兴趣的:(Java技术心得,原创经典)