mybatis解析mapper.xml文件

mybatis是怎麼讀取mapper的xml配置文件並解析其中的sql語句。java

    咱們還記得是這樣配置sqlSessionFactory的:node

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">      
   <property name="dataSource" ref="dataSource" />    
   <property name="configLocation" value="classpath:configuration.xml"></property>     
   <property name="mapperLocations" value="classpath:com/xxx/mybatis/mapper/*.xml"/>      
   <property name="typeAliasesPackage" value="com.tiantian.mybatis.model" />      
</bean>

   

    這裏配置了一個mapperLocations屬性,它是一個表達式,sqlSessionFactory會根據這個表達式讀取包com.xxx.mybaits.mapper下面的全部xml格式文件,那麼具體是怎麼根據這個屬性來讀取配置文件的呢?spring

    答案就在SqlSessionFactoryBean類中的buildSqlSessionFactory方法中:sql

if (!isEmpty(this.mapperLocations)) {  
          for (Resource mapperLocation : this.mapperLocations) {  
            if (mapperLocation == null) {  
              continue;  
            }  
      
            try {  
              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),  
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());  
              xmlMapperBuilder.parse();  
            } catch (Exception e) {  
              throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);  
            } finally {  
              ErrorContext.instance().reset();  
            }  
      
            if (logger.isDebugEnabled()) {  
              logger.debug("Parsed mapper file: '" + mapperLocation + "'");  
            }  
          }  
        }

 

    mybatis使用XMLMapperBuilder類的實例來解析mapper配置文件。數據庫

  

public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {  
    this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),  
        configuration, resource, sqlFragments);  
  } 



private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {  
    super(configuration);  
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);  
    this.parser = parser;  
    this.sqlFragments = sqlFragments;  
    this.resource = resource;  
  }

 

    接着系統調用xmlMapperBuilder的parse方法解析mapper。mybatis

 

public void parse() {  
        //若是configuration對象還沒加載xml配置文件(避免重複加載,其實是確認是否解析了mapper節點的屬性及內容,  
        //爲解析它的子節點如cache、sql、select、resultMap、parameterMap等作準備),  
        //則從輸入流中解析mapper節點,而後再將resource的狀態置爲已加載  
        if (!configuration.isResourceLoaded(resource)) {  
          configurationElement(parser.evalNode("/mapper"));  
          configuration.addLoadedResource(resource);  
          bindMapperForNamespace();  
        }  
        //解析在configurationElement函數中處理resultMap時其extends屬性指向的父對象還沒被處理的<resultMap>節點  
        parsePendingResultMaps();  
        //解析在configurationElement函數中處理cache-ref時其指向的對象不存在的<cache>節點(若是cache-ref先於其指向的cache節點加載就會出現這種狀況)  
        parsePendingChacheRefs();  
        //同上,若是cache沒加載的話處理statement時也會拋出異常  
        parsePendingStatements();  
      }

 

     mybatis解析mapper的xml文件的過程已經很明顯了,接下來咱們看看它是怎麼解析mapper的:app

private void configurationElement(XNode context) {  
        try {  
          //獲取mapper節點的namespace屬性  
          String namespace = context.getStringAttribute("namespace");  
          if (namespace.equals("")) {  
            throw new BuilderException("Mapper's namespace cannot be empty");  
          }  
          //設置當前namespace  
          builderAssistant.setCurrentNamespace(namespace);  
          //解析mapper的<cache-ref>節點  
          cacheRefElement(context.evalNode("cache-ref"));  
          //解析mapper的<cache>節點  
          cacheElement(context.evalNode("cache"));  
          //解析mapper的<parameterMap>節點  
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));  
          //解析mapper的<resultMap>節點  
          resultMapElements(context.evalNodes("/mapper/resultMap"));  
          //解析mapper的<sql>節點  
          sqlElement(context.evalNodes("/mapper/sql"));  
          //使用XMLStatementBuilder的對象解析mapper的<select>、<insert>、<update>、<delete>節點,  
          //mybaits會使用MappedStatement.Builder類build一個MappedStatement對象,  
          //因此mybaits中一個sql對應一個MappedStatement  
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));  
        } catch (Exception e) {  
          throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);  
        }  
      }

    configurationElement函數幾乎解析了mapper節點下全部子節點,至此mybaits解析了mapper中的全部節點,並將其加入到了Configuration對象中提供給sqlSessionFactory對象隨時使用。這裏咱們須要補充講一下mybaits是怎麼使用XMLStatementBuilder類的對象的parseStatementNode函數借用MapperBuilderAssistant類對象builderAssistant的addMappedStatement解析MappedStatement並將其關聯到Configuration類對象的:函數

public void parseStatementNode() {  
    //ID屬性  
    String id = context.getStringAttribute("id");  
    //databaseId屬性  
    String databaseId = context.getStringAttribute("databaseId");  
  
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {  
      return;  
    }  
    //fetchSize屬性  
    Integer fetchSize = context.getIntAttribute("fetchSize");  
    //timeout屬性  
    Integer timeout = context.getIntAttribute("timeout");  
    //parameterMap屬性  
    String parameterMap = context.getStringAttribute("parameterMap");  
    //parameterType屬性  
    String parameterType = context.getStringAttribute("parameterType");  
    Class<?> parameterTypeClass = resolveClass(parameterType);  
    //resultMap屬性  
    String resultMap = context.getStringAttribute("resultMap");  
    //resultType屬性  
    String resultType = context.getStringAttribute("resultType");  
    //lang屬性  
    String lang = context.getStringAttribute("lang");  
    LanguageDriver langDriver = getLanguageDriver(lang);  
  
    Class<?> resultTypeClass = resolveClass(resultType);  
    //resultSetType屬性  
    String resultSetType = context.getStringAttribute("resultSetType");  
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));  
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);  
  
    String nodeName = context.getNode().getNodeName();  
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));  
    //是不是<select>節點  
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;  
    //flushCache屬性  
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);  
    //useCache屬性  
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);  
    //resultOrdered屬性  
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);  
  
    // Include Fragments before parsing  
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);  
    includeParser.applyIncludes(context.getNode());  
  
    // Parse selectKey after includes and remove them.  
    processSelectKeyNodes(id, parameterTypeClass, langDriver);  
      
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)  
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);  
    //resultSets屬性  
    String resultSets = context.getStringAttribute("resultSets");  
    //keyProperty屬性  
    String keyProperty = context.getStringAttribute("keyProperty");  
    //keyColumn屬性  
    String keyColumn = context.getStringAttribute("keyColumn");  
    KeyGenerator keyGenerator;  
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;  
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);  
    if (configuration.hasKeyGenerator(keyStatementId)) {  
      keyGenerator = configuration.getKeyGenerator(keyStatementId);  
    } else {  
      //useGeneratedKeys屬性  
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",  
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))  
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();  
    }  
  
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,  
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,  
        resultSetTypeEnum, flushCache, useCache, resultOrdered,   
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);  
  }

 

     由以上代碼能夠看出mybaits使用XPath解析mapper的配置文件後將其中的resultMap、parameterMap、cache、statement等節點使用關聯的builder建立並將獲得的對象關聯到configuration對象中,而這個configuration對象能夠從sqlSession中獲取的,這就解釋了咱們在使用sqlSession對數據庫進行操做時mybaits怎麼獲取到mapper並執行其中的sql語句的問題。fetch

相關文章
相關標籤/搜索