基于spring+ibatis进行扩展

2017/12/08 框架相关

缘起

由于公司多数中间件基于ibatis2,并且在sql方面基本全部动态化,在开发阶段基本不可能知道sql最终的形态,这对测试的准确性带来了一定的困难,所以就有了这么一个Action, 对ibatis2测试环境进行扩展,在重要的测试场景能够等到最终运行的sql,用来与预期的sql进行比较。

思路

拿到问题大致看了一下ibatis2源码发现sql语句在MappedStatement可以透出,这里具体怎么通过MappedStatement得到就不深究了,这里主要记录一下解决问题的思路。那么有了这个结论问题就很明确了,我们主要拿到MappedStatement就可以了!带着这个问题我们继续从ibatis2入口深入,发现基本调用流程如下:

SqlMapClientFactoryBean->SqlMapClient->SqlMapSession->SqlMapExecutorDelegate ;所有的类似insert、update、delete…执行最终都会落到SqlMapExecutorDelegate

然而在 SqlMapExecutorDelegate提供了 getMappedStatement(String id)方法可以拿到我们需要的MappedStatement。接着我对SqlMapClientFactoryBean(一个Spring bean工厂)进行分析,发现下面这段代码:


    //spring InitializingBean 在bean创建后的处理
    public void afterPropertiesSet() throws Exception {
        if (this.lobHandler != null) {
            configTimeLobHandlerHolder.set(this.lobHandler);
        }

        try {
            // 非常关键的方法, 这里创建了 sqlMapClient,方法内容看下面。
            this.sqlMapClient = this.buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties);
            if (this.dataSource != null) {
                TransactionConfig transactionConfig = (TransactionConfig)this.transactionConfigClass.newInstance();
                DataSource dataSourceToUse = this.dataSource;
                if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {
                    dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);
                }

                transactionConfig.setDataSource((DataSource)dataSourceToUse);
                transactionConfig.initialize(this.transactionConfigProperties);
                this.applyTransactionConfig(this.sqlMapClient, transactionConfig);
            }
        } finally {
            if (this.lobHandler != null) {
                configTimeLobHandlerHolder.remove();
            }

        }

    }

    protected SqlMapClient buildSqlMapClient(Resource[] configLocations, Resource[] mappingLocations, Properties properties) throws IOException {
        if (ObjectUtils.isEmpty(configLocations)) {
            throw new IllegalArgumentException("At least 1 'configLocation' entry is required");
        } else {
            SqlMapClient client = null;
            SqlMapConfigParser configParser = new SqlMapConfigParser();
            Resource[] var9 = configLocations;
            int var8 = configLocations.length;

            for(int var7 = 0; var7 < var8; ++var7) {
                Resource configLocation = var9[var7];
                InputStream is = configLocation.getInputStream();

                try {
                    client = configParser.parse(is, properties);
                } catch (RuntimeException var13) {
                    throw new NestedIOException("Failed to parse config resource: " + configLocation, var13.getCause());
                }
            }

            if (mappingLocations != null) {
                SqlMapParser mapParser = SqlMapClientFactoryBean.SqlMapParserFactory.createSqlMapParser(configParser);
                Resource[] var14 = mappingLocations;
                int var17 = mappingLocations.length;

                for(var8 = 0; var8 < var17; ++var8) {
                    Resource mappingLocation = var14[var8];

                    try {
                        mapParser.parse(mappingLocation.getInputStream());
                    } catch (NodeletException var12) {
                        throw new NestedIOException("Failed to parse mapping resource: " + mappingLocation, var12);
                    }
                }
            }

            return client;
        }
    }

从这段代码我们明确了SqlMapClient的产出,而SqlMapClient持有SqlMapExecutorDelegateSqlMapSession,下面我们就看看这些个对象的关系:

public class SqlMapClientImpl implements SqlMapClient, ExtendedSqlMapClient {
    private static final Log log = LogFactory.getLog(SqlMapClientImpl.class);
    public SqlMapExecutorDelegate delegate;
    protected ThreadLocal localSqlMapSession = new ThreadLocal();

    public SqlMapClientImpl(SqlMapExecutorDelegate delegate) {
        this.delegate = delegate;
    }
}

看到这里估计大家思路应该就清楚了吧,我如果让SqlMapClientFactoryBean返回我自定义的SqlMapClient,我自定义的SqlMapClient使用我自定义的SqlMapExecutorDelegate,这样我就可以对SqlMapExecutorDelegate进行包装了,比如给insert、update、query这一类方法添加前置、后置处理,进而达到我们的目的了。

最后

最后呢,在这里贴一下源码,有兴趣可以看看,不过这种老版本的ibatis感觉用的人也比较少了。 源码链接

Search

    Post Directory