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

1. SqlMapClientFactoryBean

Spring中通过SqlMapClientTemplate提供对Ibatis的支持,与SpringHibernate的支持类似,SpringSqlMapClientFactoryBean就是管理IbatisIoC容器,我们首先分析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); } } }

SqlMapClientFactoryBean实现了SpringFactoryBean接口,是Spring中管理IbatisIoC容器,在IoC容器初始化过程中主要完成定位Ibatis配置文件和Ibatis映射文件等工作。同时SqlMapClientFactoryBean实现了InitializingBean接口,实现了afterPropertiesSet方法,该方法是在IoC容器初始化完成之后由IoC容器进行回调的,在该方法中主要是根据定义的Ibatis配置和映射文件创建IbatisSqlMapClient对象的过程。

2.SqlMapClientTemplate

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

(1).execute方法的实现:

JdbcTemplateHibernateTemplate一样,SpringSqlMapClientTemplate中也是通过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的方法:

我们以SpringqueryForObject方法为例,分析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,properties,jdbc,ibatis,IOC,null)