mybatis如何解析xml和加載Configuration

一、加載xml的入口

mybatis配置xml路徑的類,一般都在SqlSessionFactoryBean。怎么加載它呢?有兩種方式:

  1. xml形式:
<bean id="xxxSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
        p:dataSource-ref="xxxDataSource" p:mapperLocations="classpath*:mapper/*.xml">
        <property name="configuration">
            <bean class="org.apache.ibatis.session.Configuration">
                <property name="mapUnderscoreToCamelCase" value="true" />
            </bean>
        </property>
    </bean>
  1. @Configuration注解形式
    @Bean(name = "xxxSqlSessionFactory")
    public SqlSessionFactory xxxSqlSessionFactory(@Qualifier("xxxDataSource")  DataSource dataSource){
        SqlSessionFactory sqlSessionFactory = null;
        try {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean() ;
            sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath*:mapper/crt/*.xml"));
            sqlSessionFactoryBean.setDataSource(dataSource);
            sqlSessionFactory = sqlSessionFactoryBean.getObject();
            sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(),e);
            throw new CouponBizException(CouponBizCodeEnum.SYSTEM_ERROR) ;
        }

        return sqlSessionFactory ;
    }

不管哪種方式,最終都會(huì)執(zhí)行到sqlSessionFactoryBean.getObject()方法,得到一個(gè)SqlSessionFactory接口的實(shí)現(xiàn)類,具體是哪個(gè)實(shí)現(xiàn)類,下面會(huì)講到。好了,入口知道了,我們就開始繼續(xù)深入吧~

二、加載Configuration

應(yīng)用在啟動(dòng)的時(shí)候,Spring容器會(huì)觸發(fā)sqlSessionFactoryBean.getObject()方法得到單例對(duì)象,以便放到容器中。看下getObject方法:

 @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }

第一次必然會(huì)調(diào)用afterPropertiesSet()。繼續(xù)深入看下源碼:

@Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

sqlSessionFactory接口的實(shí)現(xiàn)類,就在buildSqlSessionFactory()里面。繼續(xù)深入:

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    //說明了mapper接口的配置,默認(rèn)用xml來實(shí)現(xiàn)
    XMLConfigBuilder xmlConfigBuilder = null;
   //上面的xml配置,我們已經(jīng)傳入configuration了,這里必然會(huì)進(jìn)來
    if (this.configuration != null) {
      configuration = this.configuration;
    //property字段,我們并沒有配置多余其他字段,所以這里都不會(huì)進(jìn)來
      if (configuration.getVariables() == null) {
        configuration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        configuration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      }
      configuration = new Configuration();
      if (this.configurationProperties != null) {
        configuration.setVariables(this.configurationProperties);
      }
    }

    //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (this.vfs != null) {
      configuration.setVfsImpl(this.vfs);
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
        }
      }
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type alias: '" + typeAlias + "'");
        }
      }
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (!isEmpty(this.plugins)) {
      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered plugin: '" + plugin + "'");
        }
      }
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
        }
      }
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (!isEmpty(this.typeHandlers)) {
      for (TypeHandler<?> typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type handler: '" + typeHandler + "'");
        }
      }
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
      try {
        configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

 //xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
    if (this.cache != null) {
      configuration.addCache(this.cache);
    }

//xmlConfigBuilder尚未初始化,無(wú)需進(jìn)來
    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
        }
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

     //xml的配置未配置該<property />標(biāo)簽,所以必為null,需要進(jìn)來
    if (this.transactionFactory == null) {
      //默認(rèn)的transactionFactory是SpringManagedTransactionFactory
      this.transactionFactory = new SpringManagedTransactionFactory();
    }
    //創(chuàng)建Environment,其實(shí)就是為了封裝dataSource和transactionFactory
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

    //xml的配置了mapperLocations,所以不為null,需要進(jìn)來
    if (!isEmpty(this.mapperLocations)) {
      //遍歷每個(gè)mapper文件
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
        //為每個(gè)xml文件,初始化一個(gè)xmlMapperBuilder。它含有configuration,目的就是容納下面parse xml得到的結(jié)果
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
        //開始解析xml文件,并把解析出來的所有標(biāo)簽,放到configuration對(duì)應(yīng)的字段上
          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 + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }

    return this.sqlSessionFactoryBuilder.build(configuration);
  }

有點(diǎn)長(zhǎng)。。。
看完里面的注釋,也就都明白了。加載configuration的核心就在xmlMapperBuilder.parse()方法里面。

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

然后進(jìn)入configurationElement方法:

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

可以看到先解析<mapper> .... </mapper>標(biāo)簽里面的東東,然后遞歸繼續(xù)解析mapper里面的<resultMap>....</resultMap>,以及<sql> ....</sql>。我們來看一下,mapper的xml標(biāo)簽格式好了。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xxx.xxx.xxxMapper">

    <resultMap id="xxxMap" type="com.xxx.xxx">
        <id column="id" property="id"/>
        <result column="uid" property="uid" />
        <result column="created_time" property="createdTime" />
        <result column="updated_time" property="updatedTime" />     
      ....
    </resultMap>

    <sql id="Base_Column_List">
        id, uid....
    </sql>

    <insert id="insert" parameterType="com.xxx.xxx">
        insert into xxx values()
    </insert>

    <select id="getById" parameterType="java.util.Map" resultType="com.xxx.xxx">
        select <include refid="Base_Column_List" />  from xxx   where id=#{id}
    </select>

    <update id="updateStatusById" parameterType="java.util.Map">
          update xxxx
          set status= #{targetStatus, jdbcType=NUMERIC}
          where id = #{id} and status = #{sourceStatus}
    </update>
</mapper>

可以看到解析的入口就在<mapper>標(biāo)簽,而它恰好就是mapper xml文件的格式。然后依次按照規(guī)范解析其余標(biāo)簽,以及標(biāo)簽里面的屬性信息,放到Configuration對(duì)應(yīng)的字段。
下面我們以解析<select>標(biāo)簽為例,解析源碼:

public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    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));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    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);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    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 {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

獲取<select>標(biāo)簽上面的屬性,很多屬性是不是很陌生。但是最常見的幾個(gè)屬性,我們一定知道。比如:id, parameterMap, resultMap。其實(shí)這些屬性在mybatis的dtd描述文件里面就有的,不信我們點(diǎn)擊xml的<select>標(biāo)簽,是可以點(diǎn)擊進(jìn)去的??吹饺缦拢?/p>

<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED 
>

id, parameterMap, resultMap等等每一個(gè)屬性都有定義好,只不過解析的時(shí)候拿出來就好了。最終所有字段解析完成后,會(huì)調(diào)用如下:

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

該方法會(huì)生成MappedStatement,它會(huì)接收<select>標(biāo)簽所有屬性,包括:真正的sql語(yǔ)句。但是我們的sql語(yǔ)句是動(dòng)態(tài)的,也就是說有條件的,只有在真正執(zhí)行的才能確定sql語(yǔ)句。那么xml的靜態(tài)的sql語(yǔ)句是怎么保存的呢?答案就在sqlSource字段。debug查看,如下:


image.png

內(nèi)容被切割為不同類型的對(duì)象了,比如:StaticTextSqlNode對(duì)象存放固定sql語(yǔ)句,如where前面的語(yǔ)句,它肯定是固定不變的,無(wú)需動(dòng)態(tài)生成。而IfSqlNode它有test字段String類型,又有contents字段,而contents又是一個(gè)StaticTextSqlNode屬于靜態(tài)不變的語(yǔ)句。
可以看出mybatis的動(dòng)態(tài)sql,會(huì)在初始化的時(shí)候生成sqlSource這種模板,后面再運(yùn)行的時(shí)候,會(huì)根據(jù)sql請(qǐng)求參數(shù),匹配這個(gè)sqlSource,最終生成要執(zhí)行的sql。

MappedStatement生成完成后,執(zhí)行configuration.addMappedStatement(statement),加入到configuration。

總結(jié):到這里我們就知道了xml的配置根據(jù)標(biāo)簽,一個(gè)一個(gè)去解析。解析完成后,最終會(huì)生成MappedStatement對(duì)象,然后把它添加到configuration。整個(gè)解析完成后,configuration是不是就擁有了所有xml的配置信息了,包括:sql語(yǔ)句,以及sql的返回值字段到對(duì)象的映射關(guān)系。這些sql執(zhí)行和sql結(jié)果映射需要的東西,全部都在configuration里面了。

下一節(jié)準(zhǔn)備分析:

  1. Mybatis中Sql解析執(zhí)行的原理是什么?
  2. Mybatis中Executor接口有幾種實(shí)現(xiàn)方式

未完待續(xù)。。。

三、Mybatis中Sql解析執(zhí)行的原理是什么?

3.1 先來介紹sql 解析的原理

可以看下面的這篇文章:
Mybatis解析動(dòng)態(tài)sql原理分析

其實(shí)上面講的更加透徹,從sqlNode的多個(gè)實(shí)現(xiàn)類來解釋。mybatis根據(jù)不同標(biāo)簽,把sql語(yǔ)句切分不同部分,然后對(duì)各個(gè)部分分別處理。如果是靜態(tài)文本,就用StaticTextSqlNode,如果是if標(biāo)簽用IfSqlNode,其他類推。就像上一節(jié)截圖那樣,最終sql語(yǔ)句會(huì)被解析問sqlSource,傳遞給configuration。

3.2 再來介紹sql 執(zhí)行的原理

當(dāng)然可以先看這個(gè)文章:mybatis調(diào)用過程
上面的文章是從mapper整個(gè)調(diào)用過程來切入的,這節(jié)我們只介紹sql具體執(zhí)行,是其中的一小塊內(nèi)容。首先我們從下面的源碼切入:

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

第一步肯定就是生成動(dòng)態(tài)sql了,如下:

public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

繼續(xù)跟進(jìn)getBoundSql方法:

@Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

rootSqlNode.apply(context)一般情況是MixedSqlNode,進(jìn)入該類:

@Override
  public boolean apply(DynamicContext context) {
    for (SqlNode sqlNode : contents) {
      sqlNode.apply(context);
    }
    return true;
  }

會(huì)根據(jù)sql語(yǔ)句不同部分,分別調(diào)用apply。apply方法完成后,最終會(huì)成為一個(gè)如下的sql語(yǔ)句:

select count(1) from xxx e left join yyy on e.order_no = rff.biz_no
        where e.sob_id = #{xxx} and e.deal_date >= #{yyy} and e.deal_date <= #{dealDateEnd} and e.active=1 

但是參數(shù)值并沒有寫進(jìn)去,說明還不是最終要執(zhí)行的sql。然后由sqlSourceParse.parse方法,會(huì)把所有'#{‘開頭的匹配出來,變成'?',得到如下的sql:

select count(1) from xxx e left join yyyy rff on e.order_no = rff.biz_no
        where e.sob_id = ? and e.deal_date >= ? and e.deal_date <= ? and e.active=1  
            and e.staff_id = ?

這個(gè)sql還是沒有參數(shù),還不是最終執(zhí)行的sql。但是它得到sqlSource變成了StaticSqlSource類型了,因?yàn)閯?dòng)態(tài)sql其實(shí)已經(jīng)構(gòu)造完成,它就變成了靜態(tài)的了,只需要綁定參數(shù)即可。

 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

到這里傳遞進(jìn)來的BoundSql都是帶有"?"的sql語(yǔ)句,看下prepareStatement方法:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

參數(shù)的綁定就發(fā)生在 handler.parameterize(stmt)里面,它會(huì)匹配參數(shù)里面的key和查詢條件的key,然后把對(duì)應(yīng)的value構(gòu)造到sql中。
就不深入進(jìn)去了,太多了。。。

至于sql的執(zhí)行,mybatis解析完成動(dòng)態(tài)sql后,它的職責(zé)就完成了。剩下的就交給dataSource的Connection去真正執(zhí)行網(wǎng)絡(luò)請(qǐng)求,通過JDBC組件,構(gòu)建mysql應(yīng)用能夠識(shí)別的應(yīng)用層協(xié)議報(bào)文,發(fā)送給服務(wù)器。然后服務(wù)器查詢得到結(jié)果后,返回給mybatis。

JDBC是如何初始化連接的,如何握手驗(yàn)證密碼的,以及如何調(diào)用的,并不在本文討論范圍。
當(dāng)然JDBC里面有一堆控制,比如超時(shí),重試,事務(wù)等等控制,并不僅僅只有網(wǎng)絡(luò)傳輸。

四、Mybatis如果進(jìn)行ORM轉(zhuǎn)換,把數(shù)據(jù)庫(kù)返回的column-value對(duì)象,通過映射轉(zhuǎn)換為我們POJO呢?

我們?nèi)缦碌拇a:

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

ps.execute(),這里應(yīng)該是阻塞式調(diào)用。函數(shù)返回,表示ps已經(jīng)收到結(jié)果了。如果對(duì)JDBC是真正如何執(zhí)行sql的,強(qiáng)烈建議跟進(jìn)去execute方法,看看mysql廠商的驅(qū)動(dòng)是怎么構(gòu)造mysql應(yīng)用層協(xié)議的。然后協(xié)議的響應(yīng),你在客戶端肯定是看不著的,你得去研究mysql的源碼才能知道。
我們這里就不展開JDBC了。剩下的我們就開始處理返回的結(jié)果,mybatis如何映射為POJO?
很明顯轉(zhuǎn)換就在handleResultSets方法。
最后給調(diào)用handleRowValues方法:

 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

最終到typeHandler.getResult(rsw.getResultSet(), columnName)方法:

typeHandler.getResult(rsw.getResultSet(), columnName)

typeHandler有很多實(shí)現(xiàn)類,比如:IntegerTypeHandler,ObjectTypeHandler等等,它會(huì)那映射關(guān)系,一個(gè)一個(gè)取出對(duì)應(yīng)列的值。

END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容