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

本文详细介绍了Spring中用于管理Ibatis的IoC容器SqlMapClientFactoryBean的工作原理,包括配置文件路径、映射文件路径、属性设置等。同时,分析了SqlMapClientTemplate如何对Ibatis的通用操作进行统一封装,特别是execute方法的实现和Spring封装IbatisAPI的方法,如queryForObject。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. SqlMapClientFactoryBean:

Spring中通过SqlMapClientTemplate提供对Ibatis的支持,与Spring对Hibernate的支持类似,Spring中SqlMapClientFactoryBean就是管理Ibatis的IoC容器,我们首先分析SqlMapClientFactoryBean的源码:

[java] view plain copy
  1. //Spring管理Ibatis的IoC容器
  2. publicclassSqlMapClientFactoryBeanimplementsFactoryBean<SqlMapClient>,InitializingBean{
  3. //当前线程绑定Ibatisblob/clob等大字段数据处理器资源
  4. privatestaticfinalThreadLocal<LobHandler>configTimeLobHandlerHolder=newThreadLocal<LobHandler>();
  5. publicstaticLobHandlergetConfigTimeLobHandler(){
  6. returnconfigTimeLobHandlerHolder.get();
  7. }
  8. //Ibatis配置文件路径
  9. privateResource[]configLocations;
  10. //Ibatis映射文件路径
  11. privateResource[]mappingLocations;
  12. //IbatissqlMapClient属性
  13. privatePropertiessqlMapClientProperties;
  14. //数据源
  15. privateDataSourcedataSource;
  16. //使用Spring事务包装数据源
  17. privatebooleanuseTransactionAwareDataSource=true;
  18. //事务配置类
  19. privateClasstransactionConfigClass=ExternalTransactionConfig.class;
  20. //事务配置属性
  21. privatePropertiestransactionConfigProperties;
  22. //blob/clob等lob类型处理器
  23. privateLobHandlerlobHandler;
  24. //IbatissqlMapClient
  25. privateSqlMapClientsqlMapClient;
  26. publicSqlMapClientFactoryBean(){
  27. this.transactionConfigProperties=newProperties();
  28. //不允许事务自动提交
  29. this.transactionConfigProperties.setProperty("SetAutoCommitAllowed","false");
  30. }
  31. //指定IbatissqlMapClient配置文件路径
  32. publicvoidsetConfigLocation(ResourceconfigLocation){
  33. this.configLocations=(configLocation!=null?newResource[]{configLocation}:null);
  34. }
  35. //指定多个sqlMapClient配置文件路径,这些配置文件在运行时合并
  36. publicvoidsetConfigLocations(Resource[]configLocations){
  37. this.configLocations=configLocations;
  38. }
  39. //指定Ibatis映射文件路径,这些映射文件在运行时被合并到SqlMapClient的配置中
  40. publicvoidsetMappingLocations(Resource[]mappingLocations){
  41. this.mappingLocations=mappingLocations;
  42. }
  43. //指定IbatisSqlMapClient可选的属性,即在SqlMapClient配置文件中通过属性
  44. //文件设置的属性
  45. publicvoidsetSqlMapClientProperties(PropertiessqlMapClientProperties){
  46. this.sqlMapClientProperties=sqlMapClientProperties;
  47. }
  48. //设置Ibatis使用的数据源
  49. publicvoidsetDataSource(DataSourcedataSource){
  50. this.dataSource=dataSource;
  51. }
  52. //设置数据源是否使用事务包装
  53. publicvoidsetUseTransactionAwareDataSource(booleanuseTransactionAwareDataSource){
  54. this.useTransactionAwareDataSource=useTransactionAwareDataSource;
  55. }
  56. //设置Ibatis使用的事务配置类,默认是//com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig
  57. publicvoidsetTransactionConfigClass(ClasstransactionConfigClass){
  58. if(transactionConfigClass==null||!TransactionConfig.class.isAssignableFrom(transactionConfigClass)){
  59. thrownewIllegalArgumentException("InvalidtransactionConfigClass:doesnotimplement"+
  60. "com.ibatis.sqlmap.engine.transaction.TransactionConfig");
  61. }
  62. this.transactionConfigClass=transactionConfigClass;
  63. }
  64. //设置Ibatis事务配置类属性
  65. publicvoidsetTransactionConfigProperties(PropertiestransactionConfigProperties){
  66. this.transactionConfigProperties=transactionConfigProperties;
  67. }
  68. //设置Ibatis使用的处理clob/blob等大字段的处理器
  69. publicvoidsetLobHandler(LobHandlerlobHandler){
  70. this.lobHandler=lobHandler;
  71. }
  72. //IoC容器初始化完成之后的回调方法,是InitializingBean接口的实现方法
  73. publicvoidafterPropertiesSet()throwsException{
  74. //配置lob处理器
  75. if(this.lobHandler!=null){
  76. configTimeLobHandlerHolder.set(this.lobHandler);
  77. }
  78. //创建Ibatis的SqlMapClient
  79. try{
  80. this.sqlMapClient=buildSqlMapClient(this.configLocations,this.mappingLocations,this.sqlMapClientProperties);
  81. //为创建的SqlMapClient设置数据源
  82. if(this.dataSource!=null){
  83. //创建事务配置实例
  84. TransactionConfigtransactionConfig=(TransactionConfig)this.transactionConfigClass.newInstance();
  85. //获取数据源
  86. DataSourcedataSourceToUse=this.dataSource;
  87. //如果Ibatis配置指定使用事务包装的数据源,并且当前获取到的数据源
  88. //不是事务包装数据源代理类型
  89. if(this.useTransactionAwareDataSource&&!(this.dataSourceinstanceofTransactionAwareDataSourceProxy)){
  90. //为指定数据源创建事务包装代理
  91. dataSourceToUse=newTransactionAwareDataSourceProxy(this.dataSource);
  92. }
  93. //事务配置对象设置数据源
  94. transactionConfig.setDataSource(dataSourceToUse);
  95. //初始化事务配置对象transactionConfig.initialize(this.transactionConfigProperties);
  96. applyTransactionConfig(this.sqlMapClient,transactionConfig);
  97. }
  98. }
  99. //创建SqlMapClient成功后,清除当前线程绑定的Lob处理器资源
  100. finally{
  101. if(this.lobHandler!=null){
  102. configTimeLobHandlerHolder.remove();
  103. }
  104. }
  105. }
  106. //具体创建SqlMapClient的方法,根据给定的Ibatis配置文件、Ibatis映射文件
  107. //和Ibatis配置中的属性文件创建SqlMapClient
  108. protectedSqlMapClientbuildSqlMapClient(
  109. Resource[]configLocations,Resource[]mappingLocations,Propertiesproperties)
  110. throwsIOException{
  111. //如果给定Ibatis配置文件路径为空
  112. if(ObjectUtils.isEmpty(configLocations)){
  113. thrownewIllegalArgumentException("Atleast1'configLocation'entryisrequired");
  114. }
  115. SqlMapClientclient=null;
  116. //创建Ibatis配置文件解析器
  117. SqlMapConfigParserconfigParser=newSqlMapConfigParser();
  118. //遍历所有的Ibatis配置文件
  119. for(ResourceconfigLocation:configLocations){
  120. //获取Ibatis配置文件输入流
  121. InputStreamis=configLocation.getInputStream();
  122. try{
  123. //创建IbatisSqlMapClient
  124. client=configParser.parse(is,properties);
  125. }
  126. catch(RuntimeExceptionex){
  127. thrownewNestedIOException("Failedtoparseconfigresource:"+configLocation,ex.getCause());
  128. }
  129. }
  130. //如果Ibatis映射文件不为null
  131. if(mappingLocations!=null){
  132. //根据Ibatis配置文件解析器创建Ibatis映射文件解析器
  133. SqlMapParsermapParser=SqlMapParserFactory.createSqlMapParser(configParser);
  134. //遍历所给定的Ibatis映射文件
  135. for(ResourcemappingLocation:mappingLocations){
  136. try{
  137. //解析Ibatis映射文件
  138. mapParser.parse(mappingLocation.getInputStream());
  139. }
  140. catch(NodeletExceptionex){
  141. thrownewNestedIOException("Failedtoparsemappingresource:"+mappingLocation,ex);
  142. }
  143. }
  144. }
  145. //返回创建的SqlMapClient对象
  146. returnclient;
  147. }
  148. //将Ibatis配置中指定的事务配置应用到SqlMapClient上
  149. protectedvoidapplyTransactionConfig(SqlMapClientsqlMapClient,TransactionConfigtransactionConfig){
  150. //如果SqlMapClient不是ExtendedSqlMapClient类型,则无法将Ibatis配置//中指定的事务配置应用到SqlMapClient对象
  151. if(!(sqlMapClientinstanceofExtendedSqlMapClient)){
  152. thrownewIllegalArgumentException(
  153. "CannotsetTransactionConfigwithDataSourceforSqlMapClientifnotoftype"+
  154. "ExtendedSqlMapClient:"+sqlMapClient);
  155. }
  156. ExtendedSqlMapClientextendedClient=(ExtendedSqlMapClient)sqlMapClient;
  157. //设置最大并发Ibatis事务数量transactionConfig.setMaximumConcurrentTransactions(extendedClient.getDelegate().getMaxTransactions());
  158. //为SqlMapClient设置事务处理器
  159. extendedClient.getDelegate().setTxManager(newTransactionManager(transactionConfig));
  160. }
  161. //SpringIoC容器中对应用提供的一个获取被管理对象的方法,应该通过此方法获
  162. //取被SpringIoC容器管理的IbatisSqlMapClient对象
  163. publicSqlMapClientgetObject(){
  164. returnthis.sqlMapClient;
  165. }
  166. //获取SqlMapClient的类型
  167. publicClass<?extendsSqlMapClient>getObjectType(){
  168. return(this.sqlMapClient!=null?this.sqlMapClient.getClass():SqlMapClient.class);
  169. }
  170. //默认SpringIoC容器中管理的对象是单态模式的
  171. publicbooleanisSingleton(){
  172. returntrue;
  173. }
  174. //Ibatis映射解析器工厂,内部类
  175. privatestaticclassSqlMapParserFactory{
  176. //创建Ibatis映射解析器
  177. publicstaticSqlMapParsercreateSqlMapParser(SqlMapConfigParserconfigParser){
  178. XmlParserStatestate=null;
  179. try{
  180. //使用JDK反射机制获取SqlMapConfigParser类中的state字段
  181. FieldstateField=SqlMapConfigParser.class.getDeclaredField("state");
  182. //使用JDK反射机制使state字段可以被访问,主要解决private、//protect和默认访问权限没有提供get方法的情况
  183. stateField.setAccessible(true);
  184. //使用Ibatis配置解析器获取指定字段的值
  185. state=(XmlParserState)stateField.get(configParser);
  186. }
  187. catch(Exceptionex){
  188. thrownewIllegalStateException("iBATIS2.3.2'state'fieldnotfoundinSqlMapConfigParserclass-"+
  189. "pleaseupgradetoIBATIS2.3.2orhigherinordertousethenew'mappingLocations'feature."+ex);
  190. }
  191. //为指定字段值创建Ibatis映射解析器
  192. returnnewSqlMapParser(state);
  193. }
  194. }
  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方法源码如下:

[java] view plain copy
  1. public<T>Texecute(SqlMapClientCallback<T>action)throwsDataAccessException{
  2. Assert.notNull(action,"Callbackobjectmustnotbenull");
  3. Assert.notNull(this.sqlMapClient,"NoSqlMapClientspecified");
  4. //通过SqlMapClient对象打开一个IbatisSqlMapSession
  5. SqlMapSessionsession=this.sqlMapClient.openSession();
  6. if(logger.isDebugEnabled()){
  7. logger.debug("OpenedSqlMapSession["+session+"]foriBATISoperation");
  8. }
  9. ConnectionibatisCon=null;
  10. try{
  11. ConnectionspringCon=null;
  12. //获取数据源
  13. DataSourcedataSource=getDataSource();
  14. //根据数据源是否是事务包装数据源代理类型,判断数据源是否需要事务包装
  15. booleantransactionAware=(dataSourceinstanceofTransactionAwareDataSourceProxy);
  16. try{
  17. //获取连接
  18. ibatisCon=session.getCurrentConnection();
  19. //如果当前IbatisSqlMapSession还没有创建过连接
  20. if(ibatisCon==null){
  21. //如果Ibatis数据源已经在Spring事务管理之下,则直接使用数据源//创建连接,否则,使用DataSourceUtils创建连接,并且创建的连//接置于Spring事务管理之中
  22. springCon=(transactionAware?
  23. dataSource.getConnection():DataSourceUtils.doGetConnection(dataSource));
  24. session.setUserConnection(springCon);
  25. if(logger.isDebugEnabled()){
  26. logger.debug("ObtainedJDBCConnection["+springCon+"]foriBATISoperation");
  27. }
  28. }
  29. //如果当前IbatisSqlMapSession已经创建过连接,则直接使用
  30. else{
  31. if(logger.isDebugEnabled()){
  32. logger.debug("ReusingJDBCConnection["+ibatisCon+"]foriBATISoperation");
  33. }
  34. }
  35. }
  36. catch(SQLExceptionex){
  37. thrownewCannotGetJdbcConnectionException("CouldnotgetJDBCConnection",ex);
  38. }
  39. //调用具体增删改查操作回调对象的方法
  40. try{
  41. returnaction.doInSqlMapClient(session);
  42. }
  43. catch(SQLExceptionex){
  44. throwgetExceptionTranslator().translate("SqlMapClientoperation",null,ex);
  45. }
  46. finally{
  47. try{
  48. //释放连接
  49. if(springCon!=null){
  50. if(transactionAware){
  51. springCon.close();
  52. }
  53. else{
  54. DataSourceUtils.doReleaseConnection(springCon,dataSource);
  55. }
  56. }
  57. }
  58. catch(Throwableex){
  59. logger.debug("CouldnotcloseJDBCConnection",ex);
  60. }
  61. }
  62. }
  63. //关闭Ibatis的SqlMapSession
  64. finally{
  65. if(ibatisCon==null){
  66. session.close();
  67. }
  68. }
  69. }

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

我们以Spring的queryForObject方法为例,分析Spring封装Ibatis API的实现,源码如下:

[java] view plain copy
  1. //查询对象
  2. publicObjectqueryForObject(finalStringstatementName,finalObjectparameterObject)
  3. throwsDataAccessException{
  4. //调用execute方法,参数是实现了SqlMapClientCallback接口的匿名内部类,
  5. //execute方法中回调该对象的doInSqlMapClient方法
  6. returnexecute(newSqlMapClientCallback<Object>(){
  7. //真正调用IbatisAPI做具体操作处理的方法
  8. publicObjectdoInSqlMapClient(SqlMapExecutorexecutor)throwsSQLException{
  9. //调用IbatisSqlMapSession对象的queryForObejct方法
  10. returnexecutor.queryForObject(statementName,parameterObject);
  11. }
  12. });
  13. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值