Mybatis-SqlSource源码解析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  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源码解析]https://www.jianshu.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>
 *     构建动态SQL
 * </p>
 * <p>
 *     代表的内容映射语句读取XML文件或一个注解它创建的SQL将被传递到数据库收到用户的输入参数
 * </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 {

  /**
   * 根据 {@code parameterObject} 构建动态SQL
   * @param parameterObject 参数对象
   * @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;

/**
 * 只创建DynamicSqlSource对象。完整可执行的sql,需要在调用getBoundSql(Parameter)方法时才能组装完成。
 * @author Clinton Begin
 */
public class DynamicSqlSource implements SqlSource {

  /**
   * mybatis配置信息
   */
  private final Configuration configuration;
  /**
   * DML标签下的子节点,封装成了MixedSqlNode
   */
  private final SqlNode rootSqlNode;

  /**
   *
   * @param configuration mybatis配置信息
   * @param rootSqlNode DML标签下的子节点,封装成了MixedSqlNode
   */
  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }

  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    /**
     * 构建动态SQL上下文,DynamicContext主要用于记录解析动态SQL语句之后产生的SQL语句片段,
     * 可以认为它是一个用于记录动态SQL语句解析结果的容器
     */
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    //解析 SQL 片段,并将解析结果存储到 DynamicContext 中
    rootSqlNode.apply(context);
    // 解析 SQL 语句,并构建 StaticSqlSource,在此过程中将 sql 语句中的占位符 #{} 替换为问号 ?,并为每个占位符构建相应的 ParameterMapping
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    // 从SqlNode 上下文中得到完成的originalSql后开始构建SqlSource
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    //调用 StaticSqlSource 的 getBoundSql 获取 BoundSql
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //将 DynamicContext 的 ContextMap 中的内容拷贝到 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;
  /**
   * 语言驱动
   */
  private final LanguageDriver languageDriver;
  /**
   * SQL provider 的对应映射器方法的方法对象
   */
  private Method providerMethod;
  /**
   * SQL provider 的对应映射器方法的方法对象的SQL脚本参数名数组
   */
  private String[] providerMethodArgumentNames;
  /**
   * SQL provider 的对应映射器方法的方法对象的参数类型数组
   */
  private Class<?>[] providerMethodParameterTypes;
  /**
   *  Sql provider 方法的上下文对象,封装着映射接口类,映射接口类方法对象, 数据库ID
   *  <p>
   *      在 SQL provider 的对应映射器方法的方法对象中有这个ProvideContext的参数类型时,才不会为null
   *  </p>
   */
  private ProviderContext providerContext;
  /**
   * SQL provider 的对应映射器方法的方法对象的ProvideContext类型参数的索引位置
   * <p>
   *     在 SQL provider 的对应映射器方法的方法对象中有这个ProvideContext的参数类型时,才不会为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注解对象指定的语言驱动,如果没有指定的话,默认使用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)) {
        /**
         * 构建providerType的实例对象,强转成ProviderMethodResolver类,构建一个ProviderContext对象,
         * 传入resolverMethod方法得到对应的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,该判断意味着出现多个匹配的方法
            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:参数名称解析器,用于构建sql脚本参数名+参数对象映射集合
    //通过ParamNameResolver获取sql脚本的参数名并赋值给providerMethodArgumentNames
    this.providerMethodArgumentNames = new ParamNameResolver(configuration, this.providerMethod).getNames();
    //获取providerMethod的参数类型数组赋值给providerMethodParameterTypes
    this.providerMethodParameterTypes = this.providerMethod.getParameterTypes();
    //遍历参数类型数组
    for (int i = 0; i < this.providerMethodParameterTypes.length; i++) {
      //获取参数
      Class<?> parameterType = this.providerMethodParameterTypes[i];
      //ProviderContext:Sql provider 方法的上下文对象,封装着映射接口类,映射接口类方法对象, 数据库ID
      //如果参数类型为ProviderContext类型
      if (parameterType == ProviderContext.class) {
        //如果providerContext不为null,这里意味着providerContext在方法中出现了多个
        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类型参数的索引位置赋值给providerContextIndex
        this.providerContextIndex = i;
      }
    }
  }

  /**
   * 传入 {@code parameterObject} ,然后获取参数映射与可执行SQL封装类对象
   * @param parameterObject 参数对象
   * @return 已绑定参数对象的参数映射与可执行SQL封装类对象
   */
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    //构建动态SQL,并绑定参数对象
    SqlSource sqlSource = createSqlSource(parameterObject);
    //传入参数对象,然后获取参数映射与可执行SQL封装类对象,然后返回出去
    return sqlSource.getBoundSql(parameterObject);
  }

  /**
   * 构建动态SQL,并绑定参数对象
   * @param parameterObject 参数对象
   * @return SQL源
   */
  private SqlSource createSqlSource(Object parameterObject) {
    try {
      //绑定参数数量等于 SQL provider 的对应映射器方法的方法对象的参数类型数组长度 -
      // 一个providerContext类型参数,如果providerContext为null,就是-0,否则就是-1
      int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);
      String sql;
      //如果SQL provider 的对应映射器方法的方法对象的参数类型数组长度为0时
      if (providerMethodParameterTypes.length == 0) {
        //执行 SQL provider 的对应映射器方法的方法得到Sql脚本
        sql = invokeProviderMethod();
        //如果绑定的参数数量为0
      } else if (bindParameterCount == 0) {
        //执行 SQL provider 的对应映射器方法的方法,并传入providerContext, 得到Sql脚本
        sql = invokeProviderMethod(providerContext);
        //如果绑定参数数量为1 且 第一个非ProviderContext类型的参数其参数类型属于parameterObject的父类或者同一个类型
      } else if (bindParameterCount == 1
           && (parameterObject == null || providerMethodParameterTypes[providerContextIndex == null || providerContextIndex == 1 ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
        /**
         * 转换  SQL provider 的对应映射器方法的方法的参数为参数对象数组,然后执行 SQL provider 的对应映射器方法的方法,
         * 并传入该参数对象数组,得到SQL脚本
         */
        sql = invokeProviderMethod(extractProviderMethodArguments(parameterObject));
        //如果参数对象是Map的子类
      } else if (parameterObject instanceof Map) {
        //强制转换参数对象为Map<String,Object>类型,并赋值params
        @SuppressWarnings("unchecked")
        Map<String, Object> params = (Map<String, Object>) parameterObject;
        /**
         * 转换  SQL provider 的对应映射器方法的方法的参数为参数对象数组,然后执行 SQL provider 的对应映射器方法的方法,
         * 并传入该参数对象数组,得到SQL脚本
         */
        sql = invokeProviderMethod(extractProviderMethodArguments(params, providerMethodArgumentNames));
      } else {
        //如果provideMethod出现多个参数(不包含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的参数类型并赋值给parameterType
      Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
      //通过语音驱动构建动态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);
    }
  }

  /**
   * 转换  SQL provider 的对应映射器方法的方法的参数为参数对象数组
   * @param parameterObject 参数对象
   * @return 存放参数的object类型数组对象
   */
  private Object[] extractProviderMethodArguments(Object parameterObject) {
    //如果方法中存在providerContext类型参数
    if (providerContext != null) {
      //定义一个长度为2的Object类型数组
      Object[] args = new Object[2];
      //将parameterObject赋值给非ProviderConetxt类型的参数索引位置的元素
      args[providerContextIndex == 0 ? 1 : 0] = parameterObject;
      //将providerContext赋值給ProviderConetxt类型的参数索引位置的元素
      args[providerContextIndex] = providerContext;
      //返回args
      return args;
      //如果方法中不存在providerContext类型参数
    } else {
      //新建一个Object类型数组对象,并将parameterObject添加到object类型数组对象中
      return new Object[] { parameterObject };
    }
  }

  /**
   * 转换  SQL provider 的对应映射器方法的方法的参数为参数对象数组
   * @param params 参数对象
   * @param argumentNames SQL provider 的对应映射器方法的方法对象的SQL脚本参数名数组
   * @return 存放参数的object类型数组对象
   */
  private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
    //新建一个长度为参数名数组长度的Object类型数组赋值给args
    Object[] args = new Object[argumentNames.length];
    //遍历args
    for (int i = 0; i < args.length; i++) {
      //如果有ProvideMethod有定义ProviderContext类型参数,且 i 等于 ProviderContext类型参数的所在索引位置
      if (providerContextIndex != null && providerContextIndex == i) {
        //将providerContext赋值给args[i]
        args[i] = providerContext;
        //如果ProvideMethod没有定义ProviderContext类型参数
      } else {
        //获取参数名对应参数对象,赋值给args[i]
        args[i] = params.get(argumentNames[i]);
      }
    }
    return args;
  }

  /**
   * 执行 SQL provider 的对应映射器方法的方法,并传入 {@code args}, 得到Sql脚本,返回返回出去
   * @param args 参数对象数组
   * @return Sql脚本字符串
   */
  private String invokeProviderMethod(Object... args) throws Exception {
    Object targetObject = null;
    //如果providerMethod不是静态方法
    if (!Modifier.isStatic(providerMethod.getModifiers())) {
      //新建一个providerType的实例对象赋值给targetObject
      targetObject = providerType.newInstance();
    }
    //直接targetObject的providerMethod方法,并传入参数,然后得到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>
 *     调用 SqlSourceBuilder类将"#{xxx}“ 替换为占位符”?",并绑定ParameterMapping,
 *     最后返回的RawSqlSource中持有一个由SqlSourceBuilder构建的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标签下的子节点,封装成MixedSqlNode
   * @param parameterType 参数类型
   */
  public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }

  /**
   *
   * @param configuration mybatis全局配置信息
   * @param sql 完整可执行SQL
   * @param parameterType 参数类型
   */
  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    //解析 SQL 语句,并构建 StaticSqlSource,在此过程中将 sql 语句中的占位符 #{} 替换为问号 ?,并为每个占位符构建相应的 ParameterMapping
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    //实际创建的为:StaticSqlSource
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }

  /**
   * 通过遍历所有的SqlNode,获取sql
   * @param configuration mybatis全局配置信息
   * @param rootSqlNode  DML标签下的子节点,封装成MixedSqlNode
   * @return 完整可执行的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;

/**
 * 静态SQL源
 * @author Clinton Begin
 */
public class StaticSqlSource implements SqlSource {

  /**
   * 可执行的完整SQL
   */
  private final String sql;
  /**
   * {@link #sql} 的参数映射集合
   */
  private final List<ParameterMapping> parameterMappings;
  /**
   * mybatis全局配置信息
   */
  private final Configuration configuration;

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

  /**
   *
   * @param configuration mybatis配置信息
   * @param sql 可执行的完整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>
 *      参数映射与可执行SQL封装类对象
 *  </p>
 *  <p>
 *       其中包含sql语句(该sql语句中可能包含 ? 这样的占位符), 以及一组parameter mapping(ParameterMapping类的实例),
 *       注意这组parameter mapping是Mybatis内部生成的(通过读取#{xx}中的内容)
 *  </p>
 *  <p>
 *      再强调一次,以上的ParameterMapping实例是在
 *      ParameterHandler接口的唯一默认实现类 DefaultParameterHandler 中被消费的.
 *  </p>
 *  <p>
 *      BoundSql更像一个中转站, Mybatis在执行一次CRUD操作过程中产生的中间数据的集中点.
 *      这一点观察其内部的字段就可以了解.
 *      内部基本没做什么处理, 只是将相应的操作调度给了内部的字段
 *  </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 {

  /**
   *  进行 #{ } 和 ${ } 替换完毕之后的结果sql, 注意每个 #{ }替换完之后就是一个 ?
   */
  private final String sql;
  /**
   *  这里的parameterMappings列表参数里的item个数, 以及每个item的属性名称等等, 都是和上面的sql中的 ? 完全一一对应的.
   */
  private final List<ParameterMapping> parameterMappings;
  /**
   * 用户传入的参数对象
   */
  private final Object parameterObject;
  /**
   * 额外的参数
   */
  private final Map<String, Object> additionalParameters;
  /**
   * 元对象参数,{@link #additionalParameters} 的元对象
   */
  private final MetaObject metaParameters;

  /**
   *
   * @param configuration mybatis全局配置信息
   * @param sql 进行 #{ } 和 ${ } 替换完毕之后的结果sql, 注意每个 #{ }替换完之后就是一个 ?
   * @param parameterMappings 参数映射
   * @param parameterObject 参数对象
   */
  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;
  }

  /**
   * 是否存在额外的参数
   * @param name 参数 如'order[0].item[0].name'
   * @return 通过 {@link PropertyTokenizer} 解析 {@code name} ,再以 {@link PropertyTokenizer#getName()}
   *  作为参数名,判断是否存在在 {@link #additionalParameters} 中
   */
  public boolean hasAdditionalParameter(String name) {
    String paramName = new PropertyTokenizer(name).getName();
    return additionalParameters.containsKey(paramName);
  }

  /**
   * 对额外的参数设置参数
   * @param name 参数属性名
   * @param value 参数值
   */
  public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
  }

  /**
   * 获取额外的参数的对应 {@code name} 的值
   * @param name 参数名
   * @return 对应 {@code name} 的值
   */
  public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容