MyBatis源码通~SqlSource

匿名 (未验证) 提交于 2019-12-02 23:34:01
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LHQJ1992/article/details/90320639

SqlSource

构建动态SQL

//XMLStatementBuilder.parseStatementNode() SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); 

涉及类

  • LanguageDriverRegistry+LanguageDriver
  • XMLStatementBuilder
  • SqlSource+SqlNode+SqlSourceBuilder
  • MappedStatement

0、 解析Statement中一些重要点

  • SqlSource如何构建?
  • Sql语句如何和参数拼接成完整的可执行sql?

1、LanguageDriverRegistry+LanguageDriver

在初始化Configuration时,会预先初始化Mybatis提供的两个LanguageDriver的实现类并注册到LanguageDriverRegistry中:RawLanguageDriverXMLLanguageDriver,默认为XMLLanguageDriver

  • LanguageDriverRegistry中用LANGUAGE_DRIVER_MAP来缓存LanguageDriver
    • key:LanguageDriver的Class
    • value:LanguageDriver的实例对象(newInstance())

1.1、XMLLanguageDriver

作用于解析select|update|insert|delete节点为完整的SQL语句,对应XML格式的配置文件,创建SqlSource、Parameterhandler

  • 主要方法1:createSqlSource(...):调用XMLScriptBuilder创建SqlSource
  • 主要方法2:createParameterHandler(...): 创建DefaultParameterHandler

1.2、RawLanguageDriver

继承自XMLLanguageDriver,RawSqlSource 语言驱动器实现类,确保创建的 SqlSource 是 RawSqlSource 类。

  • 主要方法1:createSqlSource(...):调用父类XMLLanguageDriver来创建SqlSource。
  • 主要方法2:checkIsNotDynamic(...):验证创建的SqlSource是不是RawSqlSource

2、SqlSource&SqlNode (组合设计模式)

  • 创建SqlSource时序图
  • SqlSource解析关联类图
public interface SqlSource { //通过SqlSource获得BoundSql   BoundSql getBoundSql(Object parameterObject); } 

2.1、XMLScriptBuilder

XML 动态语句( SQL )构建器,负责将 SQL 解析成 SqlSource 对象。

  • 在初始化XMLScriptBuilder时,会创建很多NodeHandler,用于解析如where、set、foreach、if等节点。
private void initNodeHandlerMap() {     nodeHandlerMap.put("trim", new TrimHandler());     nodeHandlerMap.put("where", new WhereHandler());     nodeHandlerMap.put("set", new SetHandler());     nodeHandlerMap.put("foreach", new ForEachHandler());     nodeHandlerMap.put("if", new IfHandler());     nodeHandlerMap.put("choose", new ChooseHandler());     nodeHandlerMap.put("when", new IfHandler());     nodeHandlerMap.put("otherwise", new OtherwiseHandler());     nodeHandlerMap.put("bind", new BindHandler()); } 
  • 入口方法parseScriptNode():负责将 SQL 解析成 SqlSource 对象。
    1. 调用parseDynamicTags()方法:解析 SQL 语句节点,会判断节点是是否包含一些动态标记。再根据不同节点,通过NodeHandler的handleNode处理,最后构建出SqlNode(MixedSqlNode
    • 若为动态Sql,则创建DynamicSqlSource;非动态SQL则创建RawSqlSource
      • DynamicSqlSource:只创建DynamicSqlSource对象,完整可执行的sql,需要在调用getBoundSql(Parameter)方法时才能组装完成。
      • RawSqlSource:调用 SqlSourceBuilder,#{}符 替换为占位符?,并绑定ParameterMapping,最后返回的RawSqlSource中持有一个真正的由SqlSourceBuilder构建的SqlSource对象。
        • DynamicContext:动态 SQL ,用于每次执行 SQL 操作时,记录动态 SQL 处理后的最终 SQL 字符串。
  • parseDynamicTags():不管是动态 SQL 节点还是静态 SQL 节点,都可以把它们看成是 SQL 片段,一个 SQL 语句由多个 SQL 片段组成,会将这些片段存放在contents中,最后用于创建MixedSqlNode实例。
protected MixedSqlNode parseDynamicTags(XNode node) {     List<SqlNode> contents = new ArrayList<>();     //NOTE: 遍历子节点     NodeList children = node.getNode().getChildNodes();     for (int i = 0; i < children.getLength(); i++) {       XNode child = node.newXNode(children.item(i));       //NOTE: 分支1       if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {         //NOTE: 获取文本         String data = child.getStringBody("");         TextSqlNode textSqlNode = new TextSqlNode(data);         //NOTE:  判断是否为动态sql,若其中包含"${}",则表示为动态sql         if (textSqlNode.isDynamic()) {           contents.add(textSqlNode);           isDynamic = true;         } else {           contents.add(new StaticTextSqlNode(data));         }       }       //NOTE: child 节点是 ELEMENT_NODE 类型,比如 <if>、<where> 等       else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {         String nodeName = child.getNode().getNodeName();         //NOTE: 获取对应的NodeHandler,生成相应的 SqlNode         NodeHandler handler = nodeHandlerMap.get(nodeName);         if (handler == null) {           throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");         }         handler.handleNode(child, contents);         isDynamic = true;       }     }     //NOTE: 将各种sql片段保存到MixedSqlNode中     return new MixedSqlNode(contents);   } 
  • SqlNode是如何解析的?

2.2、SqlSource

SQL 来源接口,它代表从 Mapper XML 或方法注解上,读取的一条 SQL 内容。

2.2.1、 非动态Sql,创建RawSqlSource对象

  • 首先获取sql语句
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {     DynamicContext context = new DynamicContext(configuration, null);     rootSqlNode.apply(context);     return context.getSql();   } 
  • 调用 DynamicContext:用于每次执行 SQL 操作时,记录动态 SQL 处理后的最终 SQL 字符串。
  • 将getSql()得到的originalSql,再通过SqlSourceBuilder构建StaticSqlSource对象。

2.2.2、 动态Sql,创建DynamicSqlSource对象

  • DynamicSqlSource在真正发起语句执行时,才会调SqlSourceBuilder来进行参数拼装。

2.3、SqlSourceBuilder:TODO

继承 BaseBuilder 抽象类,SqlSource的构建器,负责将 SQL 语句中的 #{} 替换成相应的 ? 占位符,并获取该 ? 占位符对应的ParameterMapping 对象。

  • 入口方法parse():执行解析原始 SQL ,关联ParameterMapping,构建 SqlSource 对象,标记是否为动态SQL?
  • 具体解析originalSql的过程在ParameterMappingTokenHandler中:TODO
  • 最后返回StaticSqlSource对象:封装SQL、configuration和ParameterMapping

2.4、DynamicContext

用于每次执行 SQL 操作时,记录动态 SQL 处理后的最终 SQL 字符串。

  • 设置 OGNL的属性访问器,实现ContextMap和ContextAccessor
  • StringJoiner sqlBuilder = new StringJoiner(" ")记录Sql,其实就是将之前解析的多个Sql片段进行合并。
  • 在每个SqlNode实现类中,会调用每个SqlNode的apply(DynamicContext context)方法,将SQL添加到StringJoiner中。
  • DynamicContext中有一个内部类ContextMap用于记录传入的参数。若传入的参数不是Map类型时,会创建对应的MetaObject对象,并封装成ContextMap对象。

3、 MappedStatement

该类中有个核心方法:getBoundSql()
其中sqlSource为MappedStatement对象对应的sql语句信息封装,在后续真正执行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);     }      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;   } 
文章来源: https://blog.csdn.net/LHQJ1992/article/details/90320639
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!