Mybatis3源码分析(三):解析mapper的xml配置文件

   这一章我们来看看mybatis是怎么读取mapper的xml配置文件并解析其中的sql语句。

    我们还记得是这样配置sqlSessionFactory的:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">      
  2.    <property name="dataSource" ref="dataSource" />    
  3.    <property name="configLocation" value="classpath:configuration.xml"></property>     
  4.    <property name="mapperLocations" value="classpath:com/xxx/mybatis/mapper/*.xml"/>      
  5.    <property name="typeAliasesPackage" value="com.tiantian.mybatis.model" />      
  6. </bean>    

    这里配置了一个mapperLocations属性,它是一个表达式,sqlSessionFactory会根据这个表达式读取包com.xxx.mybaits.mapper下面的所有xml格式文件,那么具体是怎么根据这个属性来读取配置文件的呢?

    答案就在SqlSessionFactoryBean类中的buildSqlSessionFactory方法中:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (!isEmpty(this.mapperLocations)) {  
  2.       for (Resource mapperLocation : this.mapperLocations) {  
  3.         if (mapperLocation == null) {  
  4.           continue;  
  5.         }  
  6.   
  7.         try {  
  8.           XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),  
  9.               configuration, mapperLocation.toString(), configuration.getSqlFragments());  
  10.           xmlMapperBuilder.parse();  
  11.         } catch (Exception e) {  
  12.           throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
  13.         } finally {  
  14.           ErrorContext.instance().reset();  
  15.         }  
  16.   
  17.         if (logger.isDebugEnabled()) {  
  18.           logger.debug("Parsed mapper file: '" + mapperLocation + "'");  
  19.         }  
  20.       }  
  21.     }  

    mybatis使用XMLMapperBuilder类的实例来解析mapper配置文件。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {  
  2.     this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),  
  3.         configuration, resource, sqlFragments);  
  4.   }  

  

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {  
  2.     super(configuration);  
  3.     this.builderAssistant = new MapperBuilderAssistant(configuration, resource);  
  4.     this.parser = parser;  
  5.     this.sqlFragments = sqlFragments;  
  6.     this.resource = resource;  
  7.   }  

    接着系统调用xmlMapperBuilder的parse方法解析mapper。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void parse() {  
  2.     //如果configuration对象还没加载xml配置文件(避免重复加载,实际上是确认是否解析了mapper节点的属性及内容,  
  3.     //为解析它的子节点如cache、sql、select、resultMap、parameterMap等做准备),  
  4.     //则从输入流中解析mapper节点,然后再将resource的状态置为已加载  
  5.     if (!configuration.isResourceLoaded(resource)) {  
  6.       configurationElement(parser.evalNode("/mapper"));  
  7.       configuration.addLoadedResource(resource);  
  8.       bindMapperForNamespace();  
  9.     }  
  10.     //解析在configurationElement函数中处理resultMap时其extends属性指向的父对象还没被处理的<resultMap>节点  
  11.     parsePendingResultMaps();  
  12.     //解析在configurationElement函数中处理cache-ref时其指向的对象不存在的<cache>节点(如果cache-ref先于其指向的cache节点加载就会出现这种情况)  
  13.     parsePendingChacheRefs();  
  14.     //同上,如果cache没加载的话处理statement时也会抛出异常  
  15.     parsePendingStatements();  
  16.   }  

     mybatis解析mapper的xml文件的过程已经很明显了,接下来我们看看它是怎么解析mapper的:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void configurationElement(XNode context) {  
  2.     try {  
  3.       //获取mapper节点的namespace属性  
  4.       String namespace = context.getStringAttribute("namespace");  
  5.       if (namespace.equals("")) {  
  6.         throw new BuilderException("Mapper's namespace cannot be empty");  
  7.       }  
  8.       //设置当前namespace  
  9.       builderAssistant.setCurrentNamespace(namespace);  
  10.       //解析mapper的<cache-ref>节点  
  11.       cacheRefElement(context.evalNode("cache-ref"));  
  12.       //解析mapper的<cache>节点  
  13.       cacheElement(context.evalNode("cache"));  
  14.       //解析mapper的<parameterMap>节点  
  15.       parameterMapElement(context.evalNodes("/mapper/parameterMap"));  
  16.       //解析mapper的<resultMap>节点  
  17.       resultMapElements(context.evalNodes("/mapper/resultMap"));  
  18.       //解析mapper的<sql>节点  
  19.       sqlElement(context.evalNodes("/mapper/sql"));  
  20.       //使用XMLStatementBuilder的对象解析mapper的<select>、<insert>、<update>、<delete>节点,  
  21.       //mybaits会使用MappedStatement.Builder类build一个MappedStatement对象,  
  22.       //所以mybaits中一个sql对应一个MappedStatement  
  23.       buildStatementFromContext(context.evalNodes("select|insert|update|delete"));  
  24.     } catch (Exception e) {  
  25.       throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);  
  26.     }  
  27.   }  

    configurationElement函数几乎解析了mapper节点下所有子节点,至此mybaits解析了mapper中的所有节点,并将其加入到了Configuration对象中提供给sqlSessionFactory对象随时使用。这里我们需要补充讲一下mybaits是怎么使用XMLStatementBuilder类的对象的parseStatementNode函数借用MapperBuilderAssistant类对象builderAssistant的addMappedStatement解析MappedStatement并将其关联到Configuration类对象的:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void parseStatementNode() {  
  2.     //ID属性  
  3.     String id = context.getStringAttribute("id");  
  4.     //databaseId属性  
  5.     String databaseId = context.getStringAttribute("databaseId");  
  6.   
  7.     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {  
  8.       return;  
  9.     }  
  10.     //fetchSize属性  
  11.     Integer fetchSize = context.getIntAttribute("fetchSize");  
  12.     //timeout属性  
  13.     Integer timeout = context.getIntAttribute("timeout");  
  14.     //parameterMap属性  
  15.     String parameterMap = context.getStringAttribute("parameterMap");  
  16.     //parameterType属性  
  17.     String parameterType = context.getStringAttribute("parameterType");  
  18.     Class<?> parameterTypeClass = resolveClass(parameterType);  
  19.     //resultMap属性  
  20.     String resultMap = context.getStringAttribute("resultMap");  
  21.     //resultType属性  
  22.     String resultType = context.getStringAttribute("resultType");  
  23.     //lang属性  
  24.     String lang = context.getStringAttribute("lang");  
  25.     LanguageDriver langDriver = getLanguageDriver(lang);  
  26.   
  27.     Class<?> resultTypeClass = resolveClass(resultType);  
  28.     //resultSetType属性  
  29.     String resultSetType = context.getStringAttribute("resultSetType");  
  30.     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));  
  31.     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);  
  32.   
  33.     String nodeName = context.getNode().getNodeName();  
  34.     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));  
  35.     //是否是<select>节点  
  36.     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;  
  37.     //flushCache属性  
  38.     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);  
  39.     //useCache属性  
  40.     boolean useCache = context.getBooleanAttribute("useCache", isSelect);  
  41.     //resultOrdered属性  
  42.     boolean resultOrdered = context.getBooleanAttribute("resultOrdered"false);  
  43.   
  44.     // Include Fragments before parsing  
  45.     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);  
  46.     includeParser.applyIncludes(context.getNode());  
  47.   
  48.     // Parse selectKey after includes and remove them.  
  49.     processSelectKeyNodes(id, parameterTypeClass, langDriver);  
  50.       
  51.     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)  
  52.     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);  
  53.     //resultSets属性  
  54.     String resultSets = context.getStringAttribute("resultSets");  
  55.     //keyProperty属性  
  56.     String keyProperty = context.getStringAttribute("keyProperty");  
  57.     //keyColumn属性  
  58.     String keyColumn = context.getStringAttribute("keyColumn");  
  59.     KeyGenerator keyGenerator;  
  60.     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;  
  61.     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);  
  62.     if (configuration.hasKeyGenerator(keyStatementId)) {  
  63.       keyGenerator = configuration.getKeyGenerator(keyStatementId);  
  64.     } else {  
  65.       //useGeneratedKeys属性  
  66.       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",  
  67.           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))  
  68.           ? new Jdbc3KeyGenerator() : new NoKeyGenerator();  
  69.     }  
  70.   
  71.     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,  
  72.         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,  
  73.         resultSetTypeEnum, flushCache, useCache, resultOrdered,   
  74.         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);  
  75.   }  
     由以上代码可以看出mybaits使用XPath解析mapper的配置文件后将其中的resultMap、parameterMap、cache、statement等节点使用关联的builder创建并将得到的对象关联到configuration对象中,而这个configuration对象可以从sqlSession中获取的,这就解释了我们在使用sqlSession对数据库进行操作时mybaits怎么获取到mapper并执行其中的sql语句的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值