Mybatis源码-XXXmapper.xml中的select|insert|update|delete标签解析过程

 Mybatis源码-XXXmapper.xml中的select|insert|update|delete标签解析过程

前提:上次讲过一篇《Mybatis源码-XXXmapper.xml中的resultMap标签解析过程》,现在就在上篇文章基础上讲一讲Mybatis是如何解析XXXmapper.xml文件中的select|insert|update|delete标签的,由于这几种标签的方式是一致的,下面我将以update标签为例,介绍一下update标签的解析过程。

在这里还是先贴一下需要解析的updateByPrimaryKeySelective方法:

1. 首先进入select|insert|update|delete解析入口:XMLMapperBuilder#configurationElement。

2. XMLStatementBuilder#parseStatementNode是负责解析单前的select|insert|update|delete节点,主要就是拿到节点属性去XMLLanguageDriver#createSqlSource中解析节点的子节点属性,解析完拿到SqlSource对象,将SqlSource注册到大管家中。

public void parseStatementNode() {
	// 拿到当前update标签的id属性:updateByPrimaryKeySelective
    String id = context.getStringAttribute("id");
	// 标签上配置的数据源databaseId
    String databaseId = context.getStringAttribute("databaseId");
	// requiredDatabaseId为当前默认的数据源id,只有这二个id相等后才能正确的去解析该标签
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

	// 标签上配置的parameterType属性:cn.edu.his.pay.model.entity.Admin
    String parameterType = context.getStringAttribute("parameterType");
	// 反射拿到该类的反射Clas对象
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultType = context.getStringAttribute("resultType");

	// 拿到nodeName就是对应的是什么标签,如update,insert等等,sqlCommandType非常重要,其实就是确定是那一类操作
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    // 显然现在是解析update标签而不是Select,false
	boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    // *** 重点-这里会进一步展开来看
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    // 在insert语句中我们可以在Insert标签下定义一个<selectKey>标签用于生成主键id,同样也可以自己生成 
	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;
    }

	// 将解析后的SqlSource封装到大管家中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

3. 上图代码中介绍了SqlSource的重要性,现在就更加深入的SqlSource是怎么产生的,这个时候就来到了XMLScriptBuilder#parseScriptNode方法,即就是解析当前的update标签的重点,可以看出如下的context就是当前需要解析的update类型的节点。

接着拿到update节点的信息调用XMLScriptBuilder#parseDynamicTags来解析这个节点,解析的时候主要就是判断当前节点下的属性类型,哪些是TEXT_NODE(文本节点),哪些是ELEMENT_NODE(元素节点)。当为文本节点是就直接往contents(负责封装节点信息)集合中加入一个StaticTextSqlNode。否则继续解析元素节点,并调用对于元素节点的处理类递归的去解析。

4. 这里就不再贴出set元素节点的效果图了,就是通过子节点的循环,拿到子节点信息,判断是什么类型的子节点后通过对应的子节点处理器进行解析,解析的方法就是通过反复递归调用parseDynamicTags方法来完成的,如下是Mybatis的节点处理器类图。

5. 通过所有的解析过后会得到一个最外层的SqlNode集合contents,并将contents包装到MixedSqlNode对象中个,如下是整个MixedSqlNode结构图。

通过如上结构图发现,就一个简单的update标签,通过配置文件的解析后,会得到一个三个等级的封装,对应XXXmapper.xml中的update标签如下:

6. 最后附上包装的整体类图关系

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jianhuicode

学问Chat UI(2)

前言 上文讲了下要去做哪些事,重点分析了融云Sdk中RongExtension这个扩展控件,本文来学习下同样是融云Sdk中的AutoRefreshListVie...

1896
来自专栏GreenLeaves

EF 约定介绍

当前环境为EF Code First开发模式中 一、EF默认约定 1、常用约定 (1)、当没有显示指定实体主键的时候,EF会默认将长得最像Id的属性(且类型为G...

18710
来自专栏智能大石头

MDK C++中对内联的极度优化

先来看看我们SmartIRQ的具体实现 // 智能IRQ,初始化时备份,销毁时还原 class SmartIRQ { public: force_inl...

2146
来自专栏行者常至

(14)Struts2_值栈

662
来自专栏Golang语言社区

package reflect

reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函...

963
来自专栏大内老A

.NET Core采用的全新配置系统[9]: 为什么针对XML的支持不够好?如何改进?

物理文件是我们最常用到的原始配置的载体,最佳的配置文件格式主要由三种,它们分别是JSON、XML和INI,对应的配置源类型分别是JsonConfiguratio...

1785
来自专栏个人随笔

C# 序列化与反序列化

对象持久化到文本文件,策略是:将对象的属性值打散,拆解,分别存储。 序列化:  保存对象的"全景图"  序列化是将对象转换为可保存或可传输的格式的过程  三种:...

3669
来自专栏一个会写诗的程序员的博客

前端知识体系整理(不断更新)

var x = {}; var y = []; var z = null; typeof x; // "object" typeof y; // "objec...

742
来自专栏马洪彪

Java设计模式(六)Adapter适配器模式

一、场景描述 “仪器数据采集器”包含采集数据以及发送数据给服务器两行为,则可定义“仪器数据采集器”接口,定义两方法“采集数据capture”和“发送数据send...

3375
来自专栏程序员的SOD蜜

.net访问PostgreSQL数据库发生“找不到函数名”的问题追踪

    PostgreSQL是一个使用广泛的免费开源的数据库,与MySQL比较,它更适合复杂的企业计算任务,而MySQL在互联网领域应用更为广泛,究其原因,可能...

2757

扫码关注云+社区