解决 mybatis 加载xml配置文件bug

摘要:mybatis

 

【问题描述】

                   mybatis在加载配置文件时,有可能会抛“"Could not find SQL statement to include with refid"异常信息。

           在windows下偶尔某个开发人员会遇到错误,在linux下几乎肯定会抛异常,但在aix环境下目前还未遇到此问题。

    

【问题分析】

                对于一些开源框架,我的原则是尽量不去修改它的源码,主要是为了方便今后升级减少影响。从mybatis 官方网(http://code.google.com/p/mybatis/)查找针对此问题的补丁,没找到针对此问题的修改补丁。于是对mybatis3.0.2源码进行调试,分析发现此问题是由于当mybatis在加载某个 配置文件时,当存在refid 引用到 其它配置文件中的ID时,且被引用的那个配置文件还未加载时,就抛出了 “ Could not find SQL statement to include with refid" 异常错误:

Caused By: org.apache.ibatis.builder.BuilderException: Could not find SQL statement to include with refid 'SAD02.SAD02_COL'
        at org.apache.ibatis.builder.xml.XMLStatementBuilder$IncludeNodeHandler.handleNode(XMLStatementBuilder.java:160)
        at org.apache.ibatis.builder.xml.XMLStatementBuilder.parseDynamicTags(XMLStatementBuilder.java:87)
        at org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode(XMLStatementBuilder.java:45)
        at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:192)
        at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:57)
        Truncated. see log file for complete stacktrace


  源代码位置:

        此问题应该是 mybatis 3.0.2的一个bug,而且在目前最新的版本也同样存在此问题。

 

【解决方案】

          针对此问题,目前我的解决办法是在加载配置文件时,记录那些加载过程中出错的文件,等所有文件加载之后,之前因refid引用的不存的ID在后边架载,然后再重新加载之前加载出错的文件。

          这样就解决了大多数refid id不存在的问题,但对于存在级联引用或循环引用的配置则继续报错,不建议这样引用。

          1.  修改Mybatis src/org/apache/ibatis/builder/xml/XMLMapperBuilder.java

 

                            增加属性:                         

private boolean ignoreAlreadyMapped = false;
          
            

                           增加构造方法:             

  public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments,boolean ignoreAlreadyMapped) {
      super(configuration);
      this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
      this.parser = new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver());
      this.sqlFragments = sqlFragments;
      this.resource = resource;
      this.ignoreAlreadyMapped = ignoreAlreadyMapped;
    }

                            修改 buildStatementFromContext 方法:

 private void buildStatementFromContext(List<XNode> list) {
    for (XNode context : list) {
      try {
          final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, this);
          statementParser.parseStatementNode(context);
      } catch (IllegalArgumentException e) {
          if (ignoreAlreadyMapped && e != null && e.getLocalizedMessage().indexOf("already contains value for") != -1) {

          } else {
              throw e;
          }
      }
    }


 

               如图所示:

                  

 

     2. 修改mybatis-spring-1.0.1 包中 org.springframework.orm.ibatis3.SqlSessionFactoryBean类 buildSqlSessionFactory()方法
    
          原代码块:
    
configuration.setEnvironment(environment);

        if (!ObjectUtils.isEmpty(mapperLocations)) {
            Map<String, XNode> sqlFragments = new HashMap<String, XNode>();

            for (Resource mapperLocation : mapperLocations) {
                try {
                    Reader reader = new InputStreamReader(mapperLocation.getInputStream());
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(reader, configuration, mapperLocation.getURI().toString(), sqlFragments);
                    xmlMapperBuilder.parse();
                }
                catch (Exception e) {
                    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                }

                logger.debug("Parsed mapper file: '" + mapperLocation + "'");
            }
        }
        else {
            logger.debug("Property 'mapperLocations' was not specified, only iBatis mapper files specified in the config xml were loaded");
        }

                   修改后:

configuration.setEnvironment(environment);
     List<Resource> reParse = new ArrayList<Resource>();  //Modified by herong on August 9, 2013 
if (!ObjectUtils.isEmpty(mapperLocations)) {
	Map<String, XNode> sqlFragments = new HashMap<String, XNode>();

	for (Resource mapperLocation : mapperLocations) {
		try {
			//System.out.println("Parsed mapper file: '" + mapperLocation + "'");
			logger.debug("Parsed mapper file: '" + mapperLocation + "'");  //Modified by herong on August 9, 2013 
			Reader reader = new InputStreamReader(mapperLocation.getInputStream());
			XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(reader, configuration, mapperLocation.getURI().toString(), sqlFragments);
			xmlMapperBuilder.parse();
		} catch (Exception e) {
			configuration.removeLoadedResource(mapperLocation.getURI().toString());
			reParse.add(mapperLocation);   //Modified by herong on August 9, 2013 
		}
	}
	//Modified by herong on August 9, 2013 
	//--------begin-------------
	for (Resource mapperLocation : reParse) {
		try {
			logger.debug("Parsed mapper file: '" + mapperLocation + "'");
			Reader reader = new InputStreamReader(mapperLocation.getInputStream());
			XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(reader, configuration, mapperLocation.getURI().toString(), sqlFragments,true);
			xmlMapperBuilder.parse();
		} catch (Exception e) {
			if (e != null && e.getLocalizedMessage().indexOf("Mapped Statements collection already") == -1){
				throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
			} 
		} 
	}
	 //--------end-------------


 

【附】

 mybatis补丁文件:

 http://download.csdn.net/detail/waixin/6480381

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值