Mybatis源码阅读(二):动态节点解析2.2 —— SqlSourceBuilder与三种SqlSource

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************

SqlSourceBuilder

前面我们对SqlSource和SqlNode进行了介绍,在经过SqlNode.apply方法的解析之后,Sql语句会被传递到SqlSourceBuilder中进行进一步的解析。SqlSourceBuilder主要完成了两方面的操作,一方面是解析Sql中的#{}占位符定义的属性,如jdbcType、javaType(使用较少),一方面是把#{}占位符替换成?占位符

SqlSourceBuilder代码如下

/**

  • 用于进一步解析SqlSource中的${}

  • @author Clinton Begin
    */
    public class SqlSourceBuilder extends BaseBuilder {

    private static final String PARAMETER_PROPERTIES = “javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName”;

    public SqlSourceBuilder(Configuration configuration) {
    super(configuration);
    }

    /**

    • 解析#{}
    • @param originalSql 被SqlNode.apply解析后的sql
    • @param parameterType 参数类型
    • @param additionalParameters DynamicContext.bindings集合
    • @return
      */
      public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
      ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
      // 解析#{}
      GenericTokenParser parser = new GenericTokenParser("#{", “}”, handler);
      String sql = parser.parse(originalSql);
      // 最终被解析成含有?的静态sql。创建StaticSqlSource,包含这个sql和参数
      return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
      }

    /**

    • 内部类,是解析#{}的核心
      */
      private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {

      /**

      • 记录解析后得到的parameterMapping集合
        /
        private List parameterMappings = new ArrayList<>();
        /
        *
      • 参数类型
        /
        private Class<?> parameterType;
        /
        *
      • DynamicContext.bindings对应的类
        */
        private MetaObject metaParameters;

      public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
      super(configuration);
      this.parameterType = parameterType;
      this.metaParameters = configuration.newMetaObject(additionalParameters);
      }

      public List getParameterMappings() {
      return parameterMappings;
      }

      /**

      • 占位符处理器核心方法。
      • @param content
      • @return
        */
        @Override
        public String handleToken(String content) {
        parameterMappings.add(buildParameterMapping(content));
        return “?”;
        }

      private ParameterMapping buildParameterMapping(String content) {
      Map<String, String> propertiesMap = parseParameterMapping(content);
      String property = propertiesMap.get(“property”);
      Class<?> propertyType;
      if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
      propertyType = metaParameters.getGetterType(property);
      } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
      propertyType = parameterType;
      } else if (JdbcType.CURSOR.name().equals(propertiesMap.get(“jdbcType”))) {
      propertyType = java.sql.ResultSet.class;
      } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
      propertyType = Object.class;
      } else {
      MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
      if (metaClass.hasGetter(property)) {
      propertyType = metaClass.getGetterType(property);
      } else {
      propertyType = Object.class;
      }
      }
      ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
      Class<?> javaType = propertyType;
      String typeHandlerAlias = null;
      for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
      String name = entry.getKey();
      String value = entry.getValue();
      // 处理jdbcType、javaType等一大堆东西
      if (“javaType”.equals(name)) {
      javaType = resolveClass(value);
      builder.javaType(javaType);
      } else if (“jdbcType”.equals(name)) {
      builder.jdbcType(resolveJdbcType(value));
      } else if (“mode”.equals(name)) {
      builder.mode(resolveParameterMode(value));
      } else if (“numericScale”.equals(name)) {
      builder.numericScale(Integer.valueOf(value));
      } else if (“resultMap”.equals(name)) {
      builder.resultMapId(value);
      } else if (“typeHandler”.equals(name)) {
      typeHandlerAlias = value;
      } else if (“jdbcTypeName”.equals(name)) {
      builder.jdbcTypeName(value);
      } else if (“property”.equals(name)) {
      // Do Nothing
      } else if (“expression”.equals(name)) {
      throw new BuilderException(“Expression based parameters are not supported yet”);
      } else {
      throw new BuilderException(“An invalid property '” + name + “’ was found in mapping #{” + content + "}. Valid properties are " + PARAMETER_PROPERTIES);
      }
      }
      if (typeHandlerAlias != null) {
      builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
      }
      return builder.build();
      }

      private Map<String, String> parseParameterMapping(String content) {
      try {
      return new ParameterExpression(content);
      } catch (BuilderException ex) {
      throw ex;
      } catch (Exception ex) {
      throw new BuilderException(“Parsing error was found in mapping #{” + content + "}. Check syntax #{property|(expression), var1=value1, var2=value2, …} ", ex);
      }
      }
      }

}

[点击并拖拽以移动]

其中,ParameterMappingTokenHandler是SqlSourceBuilder的一个内部类,该类是解析#{}的核心。

ParameterMapping中记录了#{}占位符中的参数属性,字段如下

public class ParameterMapping {

private Configuration configuration;/*** 参数名*/
private String property;
/*** 参数模式。输入参数还是输出参数*/
private ParameterMode mode;
private Class<?> javaType = Object.class;
private JdbcType jdbcType;
private Integer numericScale;
private TypeHandler<?> typeHandler;
private String resultMapId;
private String jdbcTypeName;
private String expression;

}

之后,SqlSourceBuilder会将Sql语句以及parameterMap平时集合封装成StaticSqlSource对象。StaticSqlSource.getBoundSql方法直接返回BoundSql,BoundSql代码如下。

/**

  • Sql实体。包含sql和 参数集合

  • @author Clinton Begin
    */
    public class BoundSql {

    private final String sql;
    /**

    • SQL中参数属性集合#{item}这些
      /
      private final List parameterMappings;
      /
      *
    • 执行SQL时传入的实际参数
      /
      private final Object parameterObject;
      /
      *
    • DynamicContext.bindings集合
      /
      private final Map<String, Object> additionalParameters;
      /
      *
    • additionalParameters对应的MetaObject
      */
      private final MetaObject metaParameters;

    public BoundSql(Configuration configuration, String sql, List parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
    }

    public String getSql() {
    return sql;
    }

    public List getParameterMappings() {
    return parameterMappings;
    }

    public Object getParameterObject() {
    return parameterObject;
    }

    public boolean hasAdditionalParameter(String name) {
    String paramName = new PropertyTokenizer(name).getName();
    return additionalParameters.containsKey(paramName);
    }

    public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
    }

    public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
    }
    }

DynamicSqlSource

DynamicSqlSource负责解析动态SQL语句,也是最常用的SqlSource实现。DynamicSqlSource使用rootSqlNode字段,记录了带解析的SqlNode根节点DynamicSqlSource的代码如下。

/**

  • 负责解析动态sql语句

  • 包含#{}占位符

  • @author Clinton Begin
    */
    public class DynamicSqlSource implements SqlSource {

    private final Configuration configuration;
    private final SqlNode rootSqlNode;

    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
    // 创建
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    // 解析sql节点
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    // 解析sql,将#{}替换成?
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
    }

}

RawSqlSource

RawSqlSource的逻辑和DynamicSqlSource类似,但是处理SQL的机制不同。RawSqlSource用于处理非动态SQL。当一个sql中只包含#{}占位符。不包含${}和动态sql节点,就不是动态SQL语句,会创建相应的StaticTextSqlNode在XmlScriptBuilder.parseScriptNode方法会判断整个SQL节点是否是动态的,如果是动态,就用DynamicSqlSource进行处理,否则用RawSqlSource进行处理。

RawSqlSource在构造方法中会调用getSql方法,该方法会调用SqlNode.apply方法完成sql语句的处不处理。SqlSourceBuilder完成占位符的替换,并返回StaticSqlSource对象。

/**

  • 处理非动态sql语句

  • 如果节点只包含“#{}”占位符,而不包含动态 SQL 点或未解析的 “${}”占位

  • 符的话, 则不是动态 SQL 语句

  • @author Eduardo Macarron

  • @since 3.2.0
    */
    public class RawSqlSource implements SqlSource {

    private final SqlSource sqlSource;

    public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
    }

    public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
    }

    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);
    }
    }

像foreach、if、where等标签,以及${}占位符,在mybatis初始化时并不知道其具体含义,因此这类sql就视为“动态sql”,交由DynamicSqlSource在程序运行时进行解析。而如果只含有#{}占位符,则会在mybatis初始化时就完成sql解析。
*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搞懂toString()与valueOf()的区别

一、toString&#xff08;&#xff09; 作用&#xff1a;toString&#xff08;&#xff09;方法返回一个表示改对象的字符串&#xff0c;如果是对象会返回&#xff0c;toString() 返回 “[object type]”,其中type是对象类型。 二、valueOf( ) 作用&#xff1a;valueOf房啊发返…

oracle入库的速度能到多少_倒车入库别练复杂了,其实就这两点

教练总会让学员反复练倒车入库&#xff0c;但不少学员都会有这样的疑惑&#xff1a;为什么每一次倒库结果都不一样&#xff0c;倒车入库的练习重点是什么&#xff1f;倒车入库是科二的重点及难点&#xff0c;但只要掌握以下两个关键&#xff0c;顺利通过真不难&#xff1a;01方…

Mybatis源码阅读(三):结果集映射3.1 —— ResultSetBuilder与简单映射

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

kdj买卖指标公式源码_通达信指标公式源码MACD背离KDJ背离指标

N1:5;N2:10;N3:21;N4:60;牛熊:EMA(CLOSE,N4),COLORGREEN,LINETHICK3;DIFF:EMA(CLOSE,12) - EMA(CLOSE,26);DEA:EMA(DIFF,8);A1:BARSLAST(REF(CROSS(DIFF,DEA),1)); B1:REF(C,A11)>C AND REF(DIFF,A11)DRAWTEXT(IF(B1>0,1,0),L-0.1,MACD底背),COLORGREEN;RSV:(CLOSE-LLV(L…

Mybatis源码阅读(三):结果集映射3.2 —— 嵌套映射

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

gridview获取选中行数据_Word转Excel,不想熬夜加班,那就掌握这个数据清洗方法...

私信回复关键词【福利】~获取丰富办公资源&#xff0c;助你高效办公早下班&#xff01;小伙伴们&#xff0c;大家好&#xff0c;我是专治各种疑难杂「数」的农夫~今天&#xff0c;我就为大家介绍一种高效的数据清洗方法&#xff0c;助你告别熬夜加班&#xff0c;拥抱美好的夜晚…

Mybatis源码阅读(三):结果集映射3.3 —— 主键生成策略

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

list最大容量_Java 基础(四)集合源码解析 List

List 接口前面我们学习了Iterator、Collection&#xff0c;为集合的学习打下了基础&#xff0c;现在我们来学习集合的第一大体系 List。List 是一个接口&#xff0c;定义了一组元素是有序的、可重复的集合。List 继承自 Collection&#xff0c;较之 Collection&#xff0c;List…

Mybatis源码阅读(四):核心接口4.1——StatementHandler

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Shell学习之结合正则表达式与通配符的使用(五)

Shell学习之结合正则表达式与通配符的使用 目录 通配符 正则表达式与通配符通配符通配符的使用正则表达式 正则表达式正则表达式的使用通配符 正则表达式与通配符 正则表达式用来在文件中匹配符合条件的字符串&#xff0c;正则是包含匹配。grep、awk、sed等命令可以支持正则表达…

Mybatis源码阅读(四):核心接口4.2——Executor(上)

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

接收xml参数_SpringBoot实战(二):接收xml请求

强烈推荐一个大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在对接一个第三方系统&#xff0c;需要接收第三方系统的回调&#xff0c;而且格式为XML形式&#xff0c;之前自己一般接收的参数是Json形式&#xff0c;于是乎做个实验验证一下使用…

报错 插入更新_window如何解决mysql数据量过大导致的报错

window如何解决报错“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中输入mysql -hlocalhost -uroot -p #如果设置了密码直接接在p 后面 show variables like %storage_engine%以下为结果可以看到InnoDB是MySQL的默认引…

Mybatis源码阅读(四):核心接口4.2——Executor(下)

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Mybatis源码阅读(五 ):接口层——SqlSession

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

插入公式_一个小工具,彻底帮你搞定在Markdown中插入公式的问题

在编辑Markdown文档时&#xff0c;插入公式是一个挺麻烦的活儿。需要掌握LaTex语法。我自己看完语法后&#xff0c;直接放弃&#xff0c;这绝对是反人类的语法。&#xff08;好吧&#xff0c;是我不会用...&#xff09;但是&#xff0c;我相信你看了这篇文章后&#xff0c;绝对…

Mybatis源码阅读(一):Mybatis初始化1.2 —— 解析别名、插件、对象工厂、反射工具箱、环境

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Google 修改 Chrome API,防止隐身模式检测

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; 在使用 Chrome 浏览网页时&#xff0c;某些网站会使用某种方法来确定访问者是否处于隐身模式&#xff0c;这是一种隐私泄漏行为。Google 目前正在考虑修改 Chrome 的相关 API&#xff0c;来杜绝…

Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

JavaScript异步基础

唯一比不知道代码为什么崩溃更可怕的事情是&#xff0c;不知道为什么一开始它是工作的&#xff01;在 ECMA 规范的最近几次版本里不断有新成员加入&#xff0c;尤其在处理异步的问题上&#xff0c;更是不断推陈出新。然而&#xff0c;我们在享受便利的同时&#xff0c;也应该了…