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 {

          //当前线程绑定Ibtis 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.dataSouce = 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 imlement " + "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);

           }

      }

      //创建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、protected和默认访问权限没有提供get方法的情况

                         stateField.setAccessible(true);

                       //使用Ibatis配置解析器获取指定字段的值

                        state = (XmlParserState) stateField.get(configParser);

                }

                catch(Exception ex){

                        throw new IllegalStateException("iBATIS 2.3.2 'state' field' 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);

             }

   }

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 ibatsCon = 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对象的queryForObject方法

                         return executor.queryForObject(statementName,parameterObject);

                    }

               });

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值