当前位置: 代码迷 >> SQL >> 《Spring技术黑幕》学习笔记13——SqlMapClientTemplate对Ibatis的封装
  详细解决方案

《Spring技术黑幕》学习笔记13——SqlMapClientTemplate对Ibatis的封装

热度:11   发布时间:2016-05-05 13:38:40.0
《Spring技术内幕》学习笔记13——SqlMapClientTemplate对Ibatis的封装
1. SqlMapClientFactoryBean :

Spring 中通过 SqlMapClientTemplate 提供对 Ibatis 的支持,与 Spring 对 Hibernate 的支持类似, Spring 中 SqlMapClientFactoryBean 就是管理 Ibatis 的 IoC 容器,我们首先分析 SqlMapClientFactoryBean 的源码:
//Spring管理Ibatis的IoC容器  
public class SqlMapClientFactoryBean implements FactoryBean<SqlMapClient>, InitializingBean { 
    //当前线程绑定Ibatis blob/clob等大字段数据处理器资源  
    private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = new ThreadLocal<LobHandler>(); 
    public static LobHandler getConfigTimeLobHandler() { 
        return configTimeLobHandlerHolder.get(); 
    } 
    //Ibatis配置文件路径  
    private Resource[] configLocations; 
    //Ibatis映射文件路径  
    private Resource[] mappingLocations; 
    //Ibatis sqlMapClient属性  
    private Properties sqlMapClientProperties; 
    //数据源  
    private DataSource dataSource; 
    //使用Spring事务包装数据源  
    private boolean useTransactionAwareDataSource = true; 
    //事务配置类  
    private Class transactionConfigClass = ExternalTransactionConfig.class; 
//事务配置属性  
    private Properties transactionConfigProperties; 
    //blob/clob等lob类型处理器  
    private LobHandler lobHandler; 
    //Ibatis sqlMapClient  
    private SqlMapClient sqlMapClient; 
    public SqlMapClientFactoryBean() { 
        this.transactionConfigProperties = new Properties(); 
    //不允许事务自动提交  
    this.transactionConfigProperties.setProperty("SetAutoCommitAllowed", "false"); 
    } 
    //指定Ibatis sqlMapClient配置文件路径  
    public void setConfigLocation(Resource configLocation) { 
        this.configLocations = (configLocation != null ? new Resource[] {configLocation} : null); 
    } 
    //指定多个sqlMapClient配置文件路径,这些配置文件在运行时合并  
    public void setConfigLocations(Resource[] configLocations) { 
        this.configLocations = configLocations; 
    } 
    //指定Ibatis映射文件路径,这些映射文件在运行时被合并到SqlMapClient的配置中  
    public void setMappingLocations(Resource[] mappingLocations) { 
        this.mappingLocations = mappingLocations; 
    } 
    //指定Ibatis SqlMapClient可选的属性,即在SqlMapClient配置文件中通过属性  
//文件设置的属性  
    public void setSqlMapClientProperties(Properties sqlMapClientProperties) { 
        this.sqlMapClientProperties = sqlMapClientProperties; 
    } 
    //设置Ibatis使用的数据源  
    public void setDataSource(DataSource dataSource) { 
        this.dataSource = dataSource; 
    } 
    //设置数据源是否使用事务包装  
    public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) { 
        this.useTransactionAwareDataSource = useTransactionAwareDataSource; 
    } 
    //设置Ibatis使用的事务配置类,默认是//com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig  
    public void setTransactionConfigClass(Class transactionConfigClass) { 
        if (transactionConfigClass == null || !TransactionConfig.class.isAssignableFrom(transactionConfigClass)) { 
            throw new IllegalArgumentException("Invalid transactionConfigClass: does not implement " + 
                    "com.ibatis.sqlmap.engine.transaction.TransactionConfig"); 
        } 
        this.transactionConfigClass = transactionConfigClass; 
    } 
    //设置Ibatis事务配置类属性  
    public void setTransactionConfigProperties(Properties transactionConfigProperties) { 
        this.transactionConfigProperties = transactionConfigProperties; 
    } 
    //设置Ibatis使用的处理clob/blob等大字段的处理器  
    public void setLobHandler(LobHandler lobHandler) { 
        this.lobHandler = lobHandler; 
    } 
    //IoC容器初始化完成之后的回调方法,是InitializingBean接口的实现方法  
    public void afterPropertiesSet() throws Exception { 
        //配置lob处理器  
        if (this.lobHandler != null) { 
            configTimeLobHandlerHolder.set(this.lobHandler); 
        } 
        //创建Ibatis的SqlMapClient  
        try { 
            this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties); 
            //为创建的SqlMapClient设置数据源  
            if (this.dataSource != null) { 
                //创建事务配置实例  
                TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance(); 
                //获取数据源  
                DataSource dataSourceToUse = this.dataSource; 
                //如果Ibatis配置指定使用事务包装的数据源,并且当前获取到的数据源  
                //不是事务包装数据源代理类型  
                if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) { 
                    //为指定数据源创建事务包装代理  
                    dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource); 
                } 
                //事务配置对象设置数据源  
                transactionConfig.setDataSource(dataSourceToUse); 
            //初始化事务配置对象 transactionConfig.initialize(this.transactionConfigProperties);  
                applyTransactionConfig(this.sqlMapClient, transactionConfig); 
            } 
        } 
        //创建SqlMapClient成功后,清除当前线程绑定的Lob处理器资源  
        finally { 
            if (this.lobHandler != null) { 
                configTimeLobHandlerHolder.remove(); 
            } 
        } 
    } 
    //具体创建SqlMapClient的方法,根据给定的Ibatis配置文件、Ibatis映射文件  
    //和Ibatis配置中的属性文件创建SqlMapClient  
    protected SqlMapClient buildSqlMapClient( 
            Resource[] configLocations, Resource[] mappingLocations, Properties properties) 
            throws IOException { 
        //如果给定Ibatis配置文件路径为空  
       if (ObjectUtils.isEmpty(configLocations)) { 
            throw new IllegalArgumentException("At least 1 'configLocation' entry is required"); 
        } 
        SqlMapClient client = null; 
        //创建Ibatis配置文件解析器  
       SqlMapConfigParser configParser = new SqlMapConfigParser(); 
        //遍历所有的Ibatis配置文件  
        for (Resource configLocation : configLocations) { 
            //获取Ibatis配置文件输入流  
            InputStream is = configLocation.getInputStream(); 
            try { 
                //创建Ibatis SqlMapClient  
                client = configParser.parse(is, properties); 
            } 
            catch (RuntimeException ex) { 
                throw new NestedIOException("Failed to parse config resource: " + configLocation, ex.getCause()); 
            } 
        } 
        //如果Ibatis映射文件不为null  
        if (mappingLocations != null) { 
            //根据Ibatis配置文件解析器创建Ibatis映射文件解析器  
            SqlMapParser mapParser = SqlMapParserFactory.createSqlMapParser(configParser); 
            //遍历所给定的Ibatis映射文件  
            for (Resource mappingLocation : mappingLocations) { 
                try { 
                    //解析Ibatis映射文件  
                    mapParser.parse(mappingLocation.getInputStream()); 
                } 
                catch (NodeletException ex) { 
                    throw new NestedIOException("Failed to parse mapping resource: " + mappingLocation, ex); 
                } 
            } 
        } 
        //返回创建的SqlMapClient对象  
        return client; 
    } 
    //将Ibatis配置中指定的事务配置应用到SqlMapClient上  
    protected void applyTransactionConfig(SqlMapClient sqlMapClient, TransactionConfig transactionConfig) { 
//如果SqlMapClient不是ExtendedSqlMapClient类型,则无法将Ibatis配置//中指定的事务配置应用到SqlMapClient对象  
        if (!(sqlMapClient instanceof ExtendedSqlMapClient)) { 
            throw new IllegalArgumentException( 
                    "Cannot set TransactionConfig with DataSource for SqlMapClient if not of type " + 
                    "ExtendedSqlMapClient: " + sqlMapClient); 
        } 
        ExtendedSqlMapClient extendedClient = (ExtendedSqlMapClient) sqlMapClient; 
    //设置最大并发Ibatis事务数量  transactionConfig.setMaximumConcurrentTransactions(extendedClient.getDelegate().getMaxTransactions());  
        //为SqlMapClient设置事务处理器  
        extendedClient.getDelegate().setTxManager(new TransactionManager(transactionConfig)); 
    } 
//Spring IoC容器中对应用提供的一个获取被管理对象的方法,应该通过此方法获  
//取被Spring IoC容器管理的Ibatis SqlMapClient对象  
    public SqlMapClient getObject() { 
        return this.sqlMapClient; 
    } 
    //获取SqlMapClient的类型  
    public Class<? extends SqlMapClient> getObjectType() { 
        return (this.sqlMapClient != null ? this.sqlMapClient.getClass() : SqlMapClient.class); 
    } 
    //默认Spring IoC容器中管理的对象是单态模式的  
    public boolean isSingleton() { 
        return true; 
    } 
//Ibatis映射解析器工厂,内部类  
    private static class SqlMapParserFactory { 
        //创建Ibatis映射解析器  
        public static SqlMapParser createSqlMapParser(SqlMapConfigParser configParser) { 
            XmlParserState state = null; 
            try { 
                //使用JDK反射机制获取SqlMapConfigParser类中的state字段  
                Field stateField = SqlMapConfigParser.class.getDeclaredField("state"); 
//使用JDK反射机制使state字段可以被访问,主要解决private、//protect和默认访问权限没有提供get方法的情况  
                stateField.setAccessible(true); 
                //使用Ibatis配置解析器获取指定字段的值  
                state = (XmlParserState) stateField.get(configParser); 
            } 
            catch (Exception ex) { 
                throw new IllegalStateException("iBATIS 2.3.2 'state' field not found in SqlMapConfigParser class - " + 
                        "please upgrade to IBATIS 2.3.2 or higher in order to use the new 'mappingLocations' feature. " + ex); 
            } 
            //为指定字段值创建Ibatis映射解析器  
            return new SqlMapParser(state); 
        } 
    } 
195.} 
SqlMapClientFactoryBean 实现了 Spring 的 FactoryBean 接口,是 Spring 中管理 Ibatis 的 IoC 容器,在 IoC 容器初始化过程中主要完成定位 Ibatis 配置文件和 Ibatis 映射文件等工作。同时 SqlMapClientFactoryBean 实现了 InitializingBean 接口,实现了 afterPropertiesSet 方法,该方法是在 IoC 容器初始化完成之后由 IoC 容器进行回调的,在该方法中主要是根据定义的 Ibatis 配置和映射文件创建 Ibatis 的 SqlMapClient 对象的过程。

2.SqlMapClientTemplate :

Spring 通过 SqlMapClientTemplate 对 Ibatis 一些通用操作做统一的封装处理,同时也对 Ibatis 的 API 做了一些封装,方便开发者使用,下面我们继续分析 SqlMapClientTemplate 对 Ibatis 封装的实现。

(1).execute 方法的实现:

同 JdbcTemplate 和 HibernateTemplate 一样, Spring 在 SqlMapClientTemplate 中也是通过 execute 方法封装 Ibatis 增删改查前的通用操作,同时在 execute 方法中调用相应的回调对象的回调方法来真正完成 Ibatis 的处理操作, execute 方法源码如下:

public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException { 
        Assert.notNull(action, "Callback object must not be null"); 
        Assert.notNull(this.sqlMapClient, "No SqlMapClient specified"); 
        //通过SqlMapClient对象打开一个Ibatis SqlMapSession  
        SqlMapSession session = this.sqlMapClient.openSession(); 
        if (logger.isDebugEnabled()) { 
            logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation"); 
        } 
        Connection ibatisCon = null; 
        try { 
            Connection springCon = null; 
            //获取数据源  
            DataSource dataSource = getDataSource(); 
            //根据数据源是否是事务包装数据源代理类型,判断数据源是否需要事务包装  
            boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy); 
            try { 
                //获取连接  
                ibatisCon = session.getCurrentConnection(); 
                //如果当前Ibatis SqlMapSession还没有创建过连接  
               if (ibatisCon == null) { 
//如果Ibatis数据源已经在Spring事务管理之下,则直接使用数据源//创建连接,否则,使用DataSourceUtils创建连接,并且创建的连//接置于Spring事务管理之中  
                    springCon = (transactionAware ? 
                            dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource)); 
                    session.setUserConnection(springCon); 
                    if (logger.isDebugEnabled()) { 
                        logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation"); 
                    } 
                } 
                //如果当前Ibatis SqlMapSession已经创建过连接,则直接使用  
                else { 
                    if (logger.isDebugEnabled()) { 
                        logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation"); 
                    } 
                } 
            } 
            catch (SQLException ex) { 
                throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); 
            } 
            //调用具体增删改查操作回调对象的方法  
            try { 
                return action.doInSqlMapClient(session); 
            } 
            catch (SQLException ex) { 
                throw getExceptionTranslator().translate("SqlMapClient operation", null, ex); 
            } 
            finally { 
                try { 
                   //释放连接  
                    if (springCon != null) { 
                        if (transactionAware) { 
                            springCon.close(); 
                        } 
                        else { 
            DataSourceUtils.doReleaseConnection(springCon, dataSource); 
                        } 
                    } 
                } 
                catch (Throwable ex) { 
                    logger.debug("Could not close JDBC Connection", ex); 
                } 
            } 
        } 
        //关闭Ibatis的SqlMapSession  
        finally { 
            if (ibatisCon == null) { 
                session.close(); 
            } 
        } 
    } 

(2).Spring 封装 Ibatis API 的方法:

我们以 Spring 的 queryForObject 方法为例,分析 Spring 封装 Ibatis API 的实现,源码如下
//查询对象  
public Object queryForObject(final String statementName, final Object parameterObject) 
            throws DataAccessException { 
        //调用execute方法,参数是实现了SqlMapClientCallback接口的匿名内部类,  
        //execute方法中回调该对象的doInSqlMapClient方法  
        return execute(new SqlMapClientCallback<Object>() { 
            //真正调用Ibatis API做具体操作处理的方法  
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { 
                //调用Ibatis SqlMapSession对象的queryForObejct方法  
                return executor.queryForObject(statementName, parameterObject); 
           } 
       }); 
    }  
  相关解决方案