`
wang4674890
  • 浏览: 87608 次
  • 性别: Icon_minigender_2
  • 来自: 厦门
社区版块
存档分类
最新评论

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

    博客分类:
  • java
阅读更多
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); 
           } 
       }); 
    }  
分享到:
评论

相关推荐

    Spring高版本对ibatis的支持

    最近想在最新的Spring5.0中集成ibatis(不是mybatis),发现已经不在支持SqlmapClientTemplate和SqlmapClientFactoryBean,于是搞了这个工具jar来进行支持如下配置 &lt;bean id="sqlMapClient" class="org.spring...

    ibatis与Spring整合例子

    Spring通过DAO模式,提供了对iBATIS的良好支持。SqlMapClient对象是iBATIS中的主要对象,我们可以通过配置让spring来管理SqlMapClient对象的创建。 与hibernate类似,Spring 提供了SqlMapClientDaoSupport对象,我们...

    springmvc-ibatis

    &lt;bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"&gt; &lt;!-- 配置 transactionManager事物管理--&gt; &lt;bean id="transactionManager" class="org.springframework....

    Spring-Reference_zh_CN(Spring中文参考手册)

    13.8. Spring对分段文件上传(multipart file upload)的支持 13.8.1. 介绍 13.8.2. 使用MultipartResolver 13.8.3. 在表单中处理分段文件上传 13.9. 使用Spring的表单标签库 13.9.1. 配置标签库 13.9.2. form标签 ...

    Spring中文帮助文档

    13.8. Spring对分段文件上传(multipart file upload)的支持 13.8.1. 介绍 13.8.2. 使用MultipartResolver 13.8.3. 在表单中处理分段文件上传 13.9. 使用Spring的表单标签库 13.9.1. 配置 13.9.2. form标签 ...

    Spring API

    13.8. Spring对分段文件上传(multipart file upload)的支持 13.8.1. 介绍 13.8.2. 使用MultipartResolver 13.8.3. 在表单中处理分段文件上传 13.9. 使用Spring的表单标签库 13.9.1. 配置 13.9.2. form标签 ...

    spring in action英文版

     4.1 学习Spring的DAO理念  4.1.1 理解Spring的DataAccessException  4.1.2 与DataSource一起工作  4.1.3 一致的DAO支持  4.2 在Spring中使用JDBC  4.2.1 JDBC代码的问题  4.2.2 使用...

    spring applicationContext 配置文件

    &lt;bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"&gt; &lt;property name="sqlMapClient"&gt; &lt;ref bean="sqlMapClient" /&gt; &lt;/property&gt; &lt;/bean&gt; &lt;!-- 配置要...

    SPRING API 2.0.CHM

    ControlFlowFactory.Jdk13ControlFlow ControlFlowFactory.Jdk14ControlFlow ControlFlowPointcut Controller Controller ControllerClassNameHandlerMapping Conventions CookieGenerator ...

Global site tag (gtag.js) - Google Analytics