Mybatis-SqlSource源碼解析

Mybatis3.5.1源碼分析

  1. Mybatis-SqlSessionFactoryBuilder微峰,XMLConfigBuilder交惯,XPathParser源碼解析
  2. Mybatis-Configuration源碼解析
  3. Mybatis-事務(wù)對象源碼解析
  4. Mybatis-數(shù)據(jù)源源碼解析
  5. Mybatis緩存策略源碼解析
  6. Mybatis-DatabaseIdProvider源碼解析
  7. Mybatis-TypeHandler源碼解析
  8. Mybatis-Reflector源碼解析
  9. Mybatis-ObjectFactory扮饶,ObjectWrapperFactory源碼分析
  10. Mybatis-Mapper各類標簽封裝類源碼解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
  12. Mybatis-MapperAnnotationBuilder源碼分析
  13. [Mybatis-MetaObject,MetaClass源碼解析]http://www.reibang.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源碼解析
  15. Mybatis-SqlSource源碼解析
  16. Mybatis-SqlNode源碼解析
  17. Mybatis-KeyGenerator源碼解析
  18. Mybatis-Executor源碼解析
  19. Mybatis-ParameterHandler源碼解析
  20. Mybatis-StatementHandler源碼解析
  21. Mybatis-DefaultResultSetHandler(一)源碼解析
  22. Mybatis-DefaultResultSetHandler(二)源碼解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源碼分析
  24. Mybatis-MapperProxy源碼解析
  25. Mybatis-SqlSession源碼解析
  26. Mybatis-Interceptor源碼解析

SqlSource

作用于XMLLanguageDriver

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

/**
 * Represents the content of a mapped statement read from an XML file or an annotation.
 * It creates the SQL that will be passed to the database out of the input parameter received from the user.
 * <p>
 *     構(gòu)建動態(tài)SQL
 * </p>
 * <p>
 *     代表的內(nèi)容映射語句讀取XML文件或一個注解它創(chuàng)建的SQL將被傳遞到數(shù)據(jù)庫收到用戶的輸入?yún)?shù)
 * </p>
 * <p>
 * 參考博客:<a herf='https://blog.csdn.net/LHQJ1992/article/details/90320639'>https://blog.csdn.net/LHQJ1992/article/details/90320639</a>
 * </p>
 * @author Clinton Begin
 */
public interface SqlSource {

  /**
   * 根據(jù) {@code parameterObject} 構(gòu)建動態(tài)SQL
   * @param parameterObject 參數(shù)對象
   * @return {@link BoundSql} 對象
   */
  BoundSql getBoundSql(Object parameterObject);

}

DynamicSqlSource

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.xmltags;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;

/**
 * 只創(chuàng)建DynamicSqlSource對象。完整可執(zhí)行的sql逆皮,需要在調(diào)用getBoundSql(Parameter)方法時才能組裝完成。
 * @author Clinton Begin
 */
public class DynamicSqlSource implements SqlSource {

  /**
   * mybatis配置信息
   */
  private final Configuration configuration;
  /**
   * DML標簽下的子節(jié)點螺男,封裝成了MixedSqlNode
   */
  private final SqlNode rootSqlNode;

  /**
   *
   * @param configuration mybatis配置信息
   * @param rootSqlNode DML標簽下的子節(jié)點,封裝成了MixedSqlNode
   */
  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    /**
     * 構(gòu)建動態(tài)SQL上下文,DynamicContext主要用于記錄解析動態(tài)SQL語句之后產(chǎn)生的SQL語句片段纵穿,
     * 可以認為它是一個用于記錄動態(tài)SQL語句解析結(jié)果的容器
     */
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    //解析 SQL 片段下隧,并將解析結(jié)果存儲到 DynamicContext 中
    rootSqlNode.apply(context);
    // 解析 SQL 語句,并構(gòu)建 StaticSqlSource谓媒,在此過程中將 sql 語句中的占位符 #{} 替換為問號 ?淆院,并為每個占位符構(gòu)建相應(yīng)的 ParameterMapping
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    // 從SqlNode 上下文中得到完成的originalSql后開始構(gòu)建SqlSource
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    //調(diào)用 StaticSqlSource 的 getBoundSql 獲取 BoundSql
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //將 DynamicContext 的 ContextMap 中的內(nèi)容拷貝到 BoundSql 中
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }

}

ProviderSqlSource

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.builder.annotation;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

import org.apache.ibatis.annotations.Lang;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
 * SQL提供者的SQL源
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class ProviderSqlSource implements SqlSource {

  /**
   * Mybatis全局配置信息
   */
  private final Configuration configuration;
  /**
   * SQL腳本提供者類
   */
  private final Class<?> providerType;
  /**
   * 語言驅(qū)動
   */
  private final LanguageDriver languageDriver;
  /**
   * SQL provider 的對應(yīng)映射器方法的方法對象
   */
  private Method providerMethod;
  /**
   * SQL provider 的對應(yīng)映射器方法的方法對象的SQL腳本參數(shù)名數(shù)組
   */
  private String[] providerMethodArgumentNames;
  /**
   * SQL provider 的對應(yīng)映射器方法的方法對象的參數(shù)類型數(shù)組
   */
  private Class<?>[] providerMethodParameterTypes;
  /**
   *  Sql provider 方法的上下文對象,封裝著映射接口類,映射接口類方法對象句惯, 數(shù)據(jù)庫ID
   *  <p>
   *      在 SQL provider 的對應(yīng)映射器方法的方法對象中有這個ProvideContext的參數(shù)類型時土辩,才不會為null
   *  </p>
   */
  private ProviderContext providerContext;
  /**
   * SQL provider 的對應(yīng)映射器方法的方法對象的ProvideContext類型參數(shù)的索引位置
   * <p>
   *     在 SQL provider 的對應(yīng)映射器方法的方法對象中有這個ProvideContext的參數(shù)類型時支救,才不會為null
   * </p>
   */
  private Integer providerContextIndex;

  /**
   * @deprecated Please use the {@link #ProviderSqlSource(Configuration, Object, Class, Method)} instead of this.
   */
  @Deprecated
  public ProviderSqlSource(Configuration configuration, Object provider) {
    this(configuration, provider, null, null);
  }

  /**
   * @since 3.4.5
   */
  /**
   *
   * @param configuration Mybatis全局配置信息
   * @param provider SQL provider 注解對象
   * @param mapperType 映射接口類
   * @param mapperMethod 映射接口類方法對象
   */
  public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
    String providerMethodName;
    try {
      this.configuration = configuration;
      //獲取mapperMethod的Lang注解
      Lang lang = mapperMethod == null ? null : mapperMethod.getAnnotation(Lang.class);
      //獲取Lang注解對象指定的語言驅(qū)動,如果沒有指定的話拷淘,默認使用XMLLanguageDriver
      this.languageDriver = configuration.getLanguageDriver(lang == null ? null : lang.value());
      //通過反射獲取SQL provider注解的type方法指定的SQL腳本提供者類
      this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
      //通過反射獲取SQL provider注解的method方法指定的SQL腳本提供者類的方法的映射方法名
      providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
      //如果方法名不是空字符 且 SQL腳本提供者是ProviderMethoResolver的子類
      if (providerMethodName.length() == 0 && ProviderMethodResolver.class.isAssignableFrom(this.providerType)) {
        /**
         * 構(gòu)建providerType的實例對象各墨,強轉(zhuǎn)成ProviderMethodResolver類,構(gòu)建一個ProviderContext對象启涯,
         * 傳入resolverMethod方法得到對應(yīng)的Sql provider 方法贬堵,賦值給providerMethod
         */
        this.providerMethod = ((ProviderMethodResolver) this.providerType.getDeclaredConstructor().newInstance())
            .resolveMethod(new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId()));
      }
      //如果provideMethod方法為null
      if (this.providerMethod == null) {
        //如果映射方法名為空字符串,就使用默認使用'provideSql'充當映射方法名;否則使用原來的
        providerMethodName = providerMethodName.length() == 0 ? "provideSql" : providerMethodName;
        //獲取SQL腳本提供者類的所有方法
        for (Method m : this.providerType.getMethods()) {
          //如果m等于映射方法名 且 m返回類型是CharSequence的子類
          if (providerMethodName.equals(m.getName()) && CharSequence.class.isAssignableFrom(m.getReturnType())) {
            //如果providerMethod不為null结洼,該判斷意味著出現(xiàn)多個匹配的方法
            if (this.providerMethod != null) {
              //拋出異常
              throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
                  + providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName()
                  + "'. Sql provider method can not overload.");
            }
            //將m賦值給providerMethod
            this.providerMethod = m;
          }
        }
      }
    } catch (BuilderException e) {
      throw e;
    } catch (Exception e) {
      throw new BuilderException("Error creating SqlSource for SqlProvider.  Cause: " + e, e);
    }
    //如果provideMethod為null,意味著在providerType中沒有找到匹配方法
    if (this.providerMethod == null) {
      //拋出異常
      throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
          + providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
    }
    //ParamNameResolver:參數(shù)名稱解析器黎做,用于構(gòu)建sql腳本參數(shù)名+參數(shù)對象映射集合
    //通過ParamNameResolver獲取sql腳本的參數(shù)名并賦值給providerMethodArgumentNames
    this.providerMethodArgumentNames = new ParamNameResolver(configuration, this.providerMethod).getNames();
    //獲取providerMethod的參數(shù)類型數(shù)組賦值給providerMethodParameterTypes
    this.providerMethodParameterTypes = this.providerMethod.getParameterTypes();
    //遍歷參數(shù)類型數(shù)組
    for (int i = 0; i < this.providerMethodParameterTypes.length; i++) {
      //獲取參數(shù)
      Class<?> parameterType = this.providerMethodParameterTypes[i];
      //ProviderContext:Sql provider 方法的上下文對象,封裝著映射接口類,映射接口類方法對象松忍, 數(shù)據(jù)庫ID
      //如果參數(shù)類型為ProviderContext類型
      if (parameterType == ProviderContext.class) {
        //如果providerContext不為null,這里意味著providerContext在方法中出現(xiàn)了多個
        if (this.providerContext != null) {
          //拋出異常
          throw new BuilderException("Error creating SqlSource for SqlProvider. ProviderContext found multiple in SqlProvider method ("
              + this.providerType.getName() + "." + providerMethod.getName()
              + "). ProviderContext can not define multiple in SqlProvider method argument.");
        }
        //新建一個ProviderContext賦值給providerContext
        this.providerContext = new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId());
        //將ProvideContext類型參數(shù)的索引位置賦值給providerContextIndex
        this.providerContextIndex = i;
      }
    }
  }

  /**
   * 傳入 {@code parameterObject} 蒸殿,然后獲取參數(shù)映射與可執(zhí)行SQL封裝類對象
   * @param parameterObject 參數(shù)對象
   * @return 已綁定參數(shù)對象的參數(shù)映射與可執(zhí)行SQL封裝類對象
   */
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    //構(gòu)建動態(tài)SQL,并綁定參數(shù)對象
    SqlSource sqlSource = createSqlSource(parameterObject);
    //傳入?yún)?shù)對象鸣峭,然后獲取參數(shù)映射與可執(zhí)行SQL封裝類對象宏所,然后返回出去
    return sqlSource.getBoundSql(parameterObject);
  }

  /**
   * 構(gòu)建動態(tài)SQL,并綁定參數(shù)對象
   * @param parameterObject 參數(shù)對象
   * @return SQL源
   */
  private SqlSource createSqlSource(Object parameterObject) {
    try {
      //綁定參數(shù)數(shù)量等于 SQL provider 的對應(yīng)映射器方法的方法對象的參數(shù)類型數(shù)組長度 -
      // 一個providerContext類型參數(shù)摊溶,如果providerContext為null楣铁,就是-0,否則就是-1
      int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);
      String sql;
      //如果SQL provider 的對應(yīng)映射器方法的方法對象的參數(shù)類型數(shù)組長度為0時
      if (providerMethodParameterTypes.length == 0) {
        //執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法得到Sql腳本
        sql = invokeProviderMethod();
        //如果綁定的參數(shù)數(shù)量為0
      } else if (bindParameterCount == 0) {
        //執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法更扁,并傳入providerContext, 得到Sql腳本
        sql = invokeProviderMethod(providerContext);
        //如果綁定參數(shù)數(shù)量為1 且 第一個非ProviderContext類型的參數(shù)其參數(shù)類型屬于parameterObject的父類或者同一個類型
      } else if (bindParameterCount == 1
           && (parameterObject == null || providerMethodParameterTypes[providerContextIndex == null || providerContextIndex == 1 ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
        /**
         * 轉(zhuǎn)換  SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組赫冬,然后執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法浓镜,
         * 并傳入該參數(shù)對象數(shù)組,得到SQL腳本
         */
        sql = invokeProviderMethod(extractProviderMethodArguments(parameterObject));
        //如果參數(shù)對象是Map的子類
      } else if (parameterObject instanceof Map) {
        //強制轉(zhuǎn)換參數(shù)對象為Map<String,Object>類型劲厌,并賦值params
        @SuppressWarnings("unchecked")
        Map<String, Object> params = (Map<String, Object>) parameterObject;
        /**
         * 轉(zhuǎn)換  SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組膛薛,然后執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法,
         * 并傳入該參數(shù)對象數(shù)組补鼻,得到SQL腳本
         */
        sql = invokeProviderMethod(extractProviderMethodArguments(params, providerMethodArgumentNames));
      } else {
        //如果provideMethod出現(xiàn)多個參數(shù)(不包含ProviderContext)哄啄,就會拋出異常.
        throw new BuilderException("Error invoking SqlProvider method ("
                + providerType.getName() + "." + providerMethod.getName()
                + "). Cannot invoke a method that holds "
                + (bindParameterCount == 1 ? "named argument(@Param)" : "multiple arguments")
                + " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
      }
      //獲取paramterObject的參數(shù)類型并賦值給parameterType
      Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
      //通過語音驅(qū)動構(gòu)建動態(tài)SQL
      return languageDriver.createSqlSource(configuration, sql, parameterType);
    } catch (BuilderException e) {
      throw e;
    } catch (Exception e) {
      throw new BuilderException("Error invoking SqlProvider method ("
          + providerType.getName() + "." + providerMethod.getName()
          + ").  Cause: " + e, e);
    }
  }

  /**
   * 轉(zhuǎn)換  SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組
   * @param parameterObject 參數(shù)對象
   * @return 存放參數(shù)的object類型數(shù)組對象
   */
  private Object[] extractProviderMethodArguments(Object parameterObject) {
    //如果方法中存在providerContext類型參數(shù)
    if (providerContext != null) {
      //定義一個長度為2的Object類型數(shù)組
      Object[] args = new Object[2];
      //將parameterObject賦值給非ProviderConetxt類型的參數(shù)索引位置的元素
      args[providerContextIndex == 0 ? 1 : 0] = parameterObject;
      //將providerContext賦值給ProviderConetxt類型的參數(shù)索引位置的元素
      args[providerContextIndex] = providerContext;
      //返回args
      return args;
      //如果方法中不存在providerContext類型參數(shù)
    } else {
      //新建一個Object類型數(shù)組對象,并將parameterObject添加到object類型數(shù)組對象中
      return new Object[] { parameterObject };
    }
  }

  /**
   * 轉(zhuǎn)換  SQL provider 的對應(yīng)映射器方法的方法的參數(shù)為參數(shù)對象數(shù)組
   * @param params 參數(shù)對象
   * @param argumentNames SQL provider 的對應(yīng)映射器方法的方法對象的SQL腳本參數(shù)名數(shù)組
   * @return 存放參數(shù)的object類型數(shù)組對象
   */
  private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
    //新建一個長度為參數(shù)名數(shù)組長度的Object類型數(shù)組賦值給args
    Object[] args = new Object[argumentNames.length];
    //遍歷args
    for (int i = 0; i < args.length; i++) {
      //如果有ProvideMethod有定義ProviderContext類型參數(shù)风范,且 i 等于 ProviderContext類型參數(shù)的所在索引位置
      if (providerContextIndex != null && providerContextIndex == i) {
        //將providerContext賦值給args[i]
        args[i] = providerContext;
        //如果ProvideMethod沒有定義ProviderContext類型參數(shù)
      } else {
        //獲取參數(shù)名對應(yīng)參數(shù)對象咨跌,賦值給args[i]
        args[i] = params.get(argumentNames[i]);
      }
    }
    return args;
  }

  /**
   * 執(zhí)行 SQL provider 的對應(yīng)映射器方法的方法,并傳入 {@code args}硼婿, 得到Sql腳本锌半,返回返回出去
   * @param args 參數(shù)對象數(shù)組
   * @return Sql腳本字符串
   */
  private String invokeProviderMethod(Object... args) throws Exception {
    Object targetObject = null;
    //如果providerMethod不是靜態(tài)方法
    if (!Modifier.isStatic(providerMethod.getModifiers())) {
      //新建一個providerType的實例對象賦值給targetObject
      targetObject = providerType.newInstance();
    }
    //直接targetObject的providerMethod方法,并傳入?yún)?shù)寇漫,然后得到SQL腳本字符串賦值給sql
    CharSequence sql = (CharSequence) providerMethod.invoke(targetObject, args);
    //如果sql不為null刊殉,返回sql;否則返回null
    return sql != null ? sql.toString() : null;
  }

}

RawSqlSource

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.defaults;

import java.util.HashMap;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.session.Configuration;

/**
 * Static SqlSource. It is faster than {@link DynamicSqlSource} because mappings are
 * calculated during startup.
 * <p>
 *     調(diào)用 SqlSourceBuilder類將"#{xxx}“ 替換為占位符”?"殉摔,并綁定ParameterMapping,
 *     最后返回的RawSqlSource中持有一個由SqlSourceBuilder構(gòu)建的SqlSource對象记焊。
 * </p>
 * @since 3.2.0
 * @author Eduardo Macarron
 */
public class RawSqlSource implements SqlSource {

  /**
   * 實際為StaticSqlSource
   */
  private final SqlSource sqlSource;

  /**
   *
   * @param configuration mybatis全局配置信息
   * @param rootSqlNode DML標簽下的子節(jié)點逸月,封裝成MixedSqlNode
   * @param parameterType 參數(shù)類型
   */
  public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }

  /**
   *
   * @param configuration mybatis全局配置信息
   * @param sql 完整可執(zhí)行SQL
   * @param parameterType 參數(shù)類型
   */
  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    //解析 SQL 語句,并構(gòu)建 StaticSqlSource遍膜,在此過程中將 sql 語句中的占位符 #{} 替換為問號 ?碗硬,并為每個占位符構(gòu)建相應(yīng)的 ParameterMapping
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    //實際創(chuàng)建的為:StaticSqlSource
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }

  /**
   * 通過遍歷所有的SqlNode,獲取sql
   * @param configuration mybatis全局配置信息
   * @param rootSqlNode  DML標簽下的子節(jié)點捌归,封裝成MixedSqlNode
   * @return 完整可執(zhí)行的sql
   */
  private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
    DynamicContext context = new DynamicContext(configuration, null);
    rootSqlNode.apply(context);
    return context.getSql();
  }

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
  }

}

StaticSqlSource

/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.builder;

import java.util.List;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;

/**
 * 靜態(tài)SQL源
 * @author Clinton Begin
 */
public class StaticSqlSource implements SqlSource {

  /**
   * 可執(zhí)行的完整SQL
   */
  private final String sql;
  /**
   * {@link #sql} 的參數(shù)映射集合
   */
  private final List<ParameterMapping> parameterMappings;
  /**
   * mybatis全局配置信息
   */
  private final Configuration configuration;

  /**
   *
   * @param configuration mybatis配置信息
   * @param sql 可執(zhí)行的完整SQL
   */
  public StaticSqlSource(Configuration configuration, String sql) {
    this(configuration, sql, null);
  }

  /**
   *
   * @param configuration mybatis配置信息
   * @param sql 可執(zhí)行的完整SQL
   * @param parameterMappings mybatis全局配置信息
   */
  public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.configuration = configuration;
  }

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return new BoundSql(configuration, sql, parameterMappings, parameterObject);
  }

}

BoundSql

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;

/**
 * An actual SQL String got from an {@link SqlSource} after having processed any dynamic content.
 * The SQL may have SQL placeholders "?" and an list (ordered) of an parameter mappings
 * with the additional information for each parameter (at least the property name of the input object to read
 * the value from).
 * <p>
 * Can also have additional parameters that are created by the dynamic language (for loops, bind...).
 *  <p>
 *      參數(shù)映射與可執(zhí)行SQL封裝類對象
 *  </p>
 *  <p>
 *       其中包含sql語句(該sql語句中可能包含 ? 這樣的占位符), 以及一組parameter mapping(ParameterMapping類的實例),
 *       注意這組parameter mapping是Mybatis內(nèi)部生成的(通過讀取#{xx}中的內(nèi)容)
 *  </p>
 *  <p>
 *      再強調(diào)一次,以上的ParameterMapping實例是在
 *      ParameterHandler接口的唯一默認實現(xiàn)類 DefaultParameterHandler 中被消費的.
 *  </p>
 *  <p>
 *      BoundSql更像一個中轉(zhuǎn)站, Mybatis在執(zhí)行一次CRUD操作過程中產(chǎn)生的中間數(shù)據(jù)的集中點.
 *      這一點觀察其內(nèi)部的字段就可以了解.
 *      內(nèi)部基本沒做什么處理, 只是將相應(yīng)的操作調(diào)度給了內(nèi)部的字段
 *  </p>
 *  <p>
 *      <a herf='https://blog.csdn.net/lqzkcx3/article/details/78370497'>https://blog.csdn.net/lqzkcx3/article/details/78370497</a>
 *  </p>
 * @author Clinton Begin
 */
public class BoundSql {

  /**
   *  進行 #{ } 和 ${ } 替換完畢之后的結(jié)果sql, 注意每個 #{ }替換完之后就是一個 ?
   */
  private final String sql;
  /**
   *  這里的parameterMappings列表參數(shù)里的item個數(shù), 以及每個item的屬性名稱等等, 都是和上面的sql中的 ? 完全一一對應(yīng)的.
   */
  private final List<ParameterMapping> parameterMappings;
  /**
   * 用戶傳入的參數(shù)對象
   */
  private final Object parameterObject;
  /**
   * 額外的參數(shù)
   */
  private final Map<String, Object> additionalParameters;
  /**
   * 元對象參數(shù)肛响,{@link #additionalParameters} 的元對象
   */
  private final MetaObject metaParameters;

  /**
   *
   * @param configuration mybatis全局配置信息
   * @param sql 進行 #{ } 和 ${ } 替換完畢之后的結(jié)果sql, 注意每個 #{ }替換完之后就是一個 ?
   * @param parameterMappings 參數(shù)映射
   * @param parameterObject 參數(shù)對象
   */
  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }

  /**
   *
   * @return {@see #sql}
   */
  public String getSql() {
    return sql;
  }

  /**
   *
   * @return {@see #parameterMappings}
   */
  public List<ParameterMapping> getParameterMappings() {
    return parameterMappings;
  }

  /**
   *
   * @return {@see #parameterObject}
   */
  public Object getParameterObject() {
    return parameterObject;
  }

  /**
   * 是否存在額外的參數(shù)
   * @param name 參數(shù) 如'order[0].item[0].name'
   * @return 通過 {@link PropertyTokenizer} 解析 {@code name} ,再以 {@link PropertyTokenizer#getName()}
   *  作為參數(shù)名,判斷是否存在在 {@link #additionalParameters} 中
   */
  public boolean hasAdditionalParameter(String name) {
    String paramName = new PropertyTokenizer(name).getName();
    return additionalParameters.containsKey(paramName);
  }

  /**
   * 對額外的參數(shù)設(shè)置參數(shù)
   * @param name 參數(shù)屬性名
   * @param value 參數(shù)值
   */
  public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
  }

  /**
   * 獲取額外的參數(shù)的對應(yīng) {@code name} 的值
   * @param name 參數(shù)名
   * @return 對應(yīng) {@code name} 的值
   */
  public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
  }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惜索,一起剝皮案震驚了整個濱河市特笋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巾兆,老刑警劉巖猎物,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異角塑,居然都是意外死亡蔫磨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門圃伶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堤如,“玉大人,你說我怎么就攤上這事窒朋〔蟀眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵侥猩,是天一觀的道長榔至。 經(jīng)常有香客問我,道長欺劳,這世上最難降的妖魔是什么唧取? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮划提,結(jié)果婚禮上枫弟,老公的妹妹穿的比我還像新娘。我一直安慰自己腔剂,他們只是感情好媒区,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般袜漩。 火紅的嫁衣襯著肌膚如雪绪爸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天宙攻,我揣著相機與錄音奠货,去河邊找鬼。 笑死座掘,一個胖子當著我的面吹牛递惋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播溢陪,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼萍虽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了形真?” 一聲冷哼從身側(cè)響起杉编,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咆霜,沒想到半個月后邓馒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蛾坯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年光酣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脉课。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡救军,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出倘零,到底是詐尸還是另有隱情缤言,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布视事,位于F島的核電站,受9級特大地震影響庆揩,放射性物質(zhì)發(fā)生泄漏俐东。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一订晌、第九天 我趴在偏房一處隱蔽的房頂上張望虏辫。 院中可真熱鬧,春花似錦锈拨、人聲如沸砌庄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娄昆。三九已至佩微,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萌焰,已是汗流浹背哺眯。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扒俯,地道東北人奶卓。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像撼玄,于是被迫代替她去往敵國和親夺姑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容