【Mybatis源码分析】解析语句标签_Select|Update|Insert|Delete

解析语句标签 Select|Update|Insert|Delete

  • 一、前言
  • 二、语句标签的源码分析
  • 三、sql 标签的解析
  • 四、总结

一、前言

在阐述解析语句标签之前,得先知道我们的语句标签内容最后被封装到Configuration哪?(都应该知道 Mybatis 通过的是 XMLConfigBuilder 去解析 xml 然后封装到Configuration对象中传递给 SqlSessionFactory 去往下执行)。

而语句标签的解析内容,即被封装到了 Configuration 中的 mappedStatements Map对象中,即如下属性:

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
      "Mapped Statements collection")
          .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and "
              + targetValue.getResource());

其 key 对应的是标签 id,而 MappedStatement 是对语句标签内容的封装实例类(它是一个 final 类,最终是通过内部的 Builder 类 build 构建出来的,未向外提供构造),你可以理解为一条 Select|Update|Insert|Delete 语句标签映射着一个 MappedStatement,就如同一个ResultMap 字段对应着一个 ResultMapping 一样。内部属性如下:

  private String resource;// 源自于哪个mapper文件
  private Configuration configuration;// 配置文件
  private String id;// id
  private Integer fetchSize;// 每次从服务器获取的数量
  private Integer timeout;// 最长访问数据库的时间
  private StatementType statementType;// STATEMENT、PREPARED、CALLABLE
  private ResultSetType resultSetType;
  private SqlSource sqlSource;// 对SQL的包装
  private Cache cache;// 缓存
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;// 结果映射
  private boolean flushCacheRequired;// 是否需要缓存刷新
  private boolean useCache;// 是否使用二级缓存
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;// UNKNOWN、INSERT、UPDATE、SELECT
  private KeyGenerator keyGenerator;// 主键生成器
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;// 是否存在嵌套查询结果集
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;// 语言驱动,永磊解析动态或静态SQL
  private String[] resultSets;// resultset
  private boolean dirtySelect;

很多属性都是大伙熟知的,像 SqlSource、LanguageDriver 在我之前的博客中也有对其的源码解析,后者是对前者的获取,前者是获取最终要执行的 sql 的封装(boundsql)。【Mybatis源码分析】动态标签的底层原理,DynamicSqlSource源码分析

大概流程就如下图这样:
在这里插入图片描述

前言先到这,开始真正的源码分析。

二、语句标签的源码分析

这边的话由于是博客,不会从头到尾对源码进行分析,只对核心部分进行解析,在进行之前,先对 Mybatis 对 xml 解析时,使用到的构造器总览构个图,了解了整体对源码分析更有帮助(自身观看源码后得出来的,如有问题,评论区可以指出)。(图虽然直观,但想出来感觉太酷了,也不知道如果我来设计的话要吃几个核桃才能设计出来)

请添加图片描述

解析语句标签的核心内容在 XMLStatementBuilder.parseStatementNode 方法中,解析完标签内容然后将其通过 MapperBuilderAssistant 对象映射成 MapperStatement 然后封装到 Configuration 中的 mappedStatements 中。

核心源码如下-有删减(可以大致看看,细节或者说主要部分,下面有图片详细解释):

public void parseStatementNode() {
    String id = context.getStringAttribute("id");

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    // 这是对 sql 片段的引用,在下面对 sql 标签进行了源码分析
    // 这里是通过 include 标签对 sql 标签内容的引用
    // 可以说是替换内容吧
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
		
	// 这里的话可以自定义 LanguageDriver 对象然后去使用然后获取对应的 SqlSource对象
	// 这里的话默认是 XMLLanguageDriver
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    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 sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

	// 去得到最后执行语句准备使用的语句类型,默认是 PreparedStatement
    StatementType statementType = StatementType
        .valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

    String parameterMap = context.getStringAttribute("parameterMap");
    
    // 获取返回值类型
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    if (resultTypeClass == null && resultMap == null) {
      resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);
    }
   
   // 通过助手去构建 MappedStatement,
   // 并且封装进 configuration 中的 mappedStatements中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
        parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
  }

测试的 xml:

    <sql id="person_test">
        id,name,age,sex
    </sql>

    <select id="hhh" resultType="com.ncpowernode.mybatis.bean.Person">
        select <include refid="person_test"/>
        from t_person
    </select>

下面我认为构建过程中几个主要的属性解析特地说明一下:

你没配置 lang 属性的话,默认就是通过 XMLLanguageDriver 进行解析的。

在这里插入图片描述按照测试的语句的话,我是没用使用动态SQL的,所以SQLSource解析结果应该是RawSQLSource对象。

在这里插入图片描述

没有去指定对应的 StatementType 语句类型的话,默认就是使用 PreparedStatement。

在这里插入图片描述

如果是 Select 语句标签的话,需要指定 resultType 属性或 resultMap 的原因(都没指定的话,就把 namespace 指定的类当做返回对象):

在这里插入图片描述

而后就通过 Mapper 助手去构建 MappedStatement 对象,并且映射咯。

在这里插入图片描述
即通过 MappedStatement.Builder 进行构建,然后封装到 configuration 中。

在这里插入图片描述
封装的话,就直接 put(全限定id,MappedStatement对象咯)

在这里插入图片描述

三、sql 标签的解析

测试代码如下:

	<sql id="person_test">
        id,name,age,sex
    </sql>

    <select id="hhh" resultType="com.ncpowernode.mybatis.bean.Person">
        select <include refid="person_test"/>
        from t_person
    </select>    
    @Test
    public void testSqlTag(){
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        List<Person> xx = mapper.hhh();
    }

核心解析源码如下(本质就是将 sql 片段的内容封装到 sqlFragments 这个 map 中,然后后续供语句标签里使用)

  private void sqlElement(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      String databaseId = context.getStringAttribute("databaseId");
      String id = context.getStringAttribute("id");
      // 合并成全id,namespace.id 这种形式,全限定id
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        sqlFragments.put(id, context);
      }
    }
  }

四、总结

  • 其实通过这些源码分析也知道了:为什么默认是使用 XMLLanguageDriver 去获取 SQLSource;为什么默认是使用 PreparedStatement 去操纵最后的 SQL;查询Select语句中,不指定resultType 、resultMap 属性行不行?行,但默认就是返回你 namespace 指定的类。
  • 我们可以通过配置标签的 lang 属性,来指定对应的 LanguageDriver 实现类去获取 SqlSource.当然我觉得默认的已经很棒了,这个应该用不着自己写。
  • 在 mapper 文件中,使用了 sql 标签,可以使用 include 标签去使用 sql 标签中的内容。

下面我画的俩图感觉还挺形象:
在这里插入图片描述

请添加图片描述

这里说点题外话:在 XMLScriptBuilder 中解析动态标签时,${} 也是被解析成动态sql,对应的动态 SQLNode 是 TextSqlNode,内部 apply 方法是通过 GenericTokenParser 解析完然后封装到 DynamicContext 中的。而解析 #{} 时是不被算作为动态 sql 的,这是因为不管是 RawSqlSource 还是 DynamicSqlSource,都会通过 SqlSourceBuilder.parse 方法对 #{} 进行处理。(在上次博客中我自己也是理解的模模糊糊,这里明确后所以再指明一下)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Postman返回了一个html页面

问题记录 调用公司的测试环境接口&#xff0c;从浏览器控制台接口处cCopy as cURL(cmd)&#xff0c;获取完整的请求内容&#xff0c;然后导入postman发起请求 提测时发现返回一个html页面&#xff0c;明显是被请求在网管处被拦截了&#xff0c;网关返回的这个报错html页面 …

macOS(m1/m2)破解Sublime Text和Navicat16

破解Sublime Text 说明&#xff1a;全程使用的是终端操作 1. 下载Sublime Text&#xff0c;建议使用brew下载 2. 进入到下载的app的文件夹 cd "/Applications/Sublime Text.app/Contents/MacOS/"3. 执行以下操作以确认版本是否匹配 md5 -q sublime_text | grep -i…

STM32 CubeMX (第二步Freertos任务通信:队列、信号量、互斥量,事件组,任务通知)

STM32 CubeMX STM32 CubeMX ____Freertos任务通信&#xff1a;队列、信号量、互斥量&#xff0c;事件组&#xff0c;任务通知 STM32 CubeMX一、STM32 CubeMX设置时钟配置HAL时基选择TIM1&#xff08;不要选择滴答定时器&#xff1b;滴答定时器留给OS系统做时基&#xff09;使用…

MYSQL完全卸载、安装与账号创建、权限控制

一、卸载mysql CentOS 卸载 MySQL 1. 查看安装情况 使用以下命令查看当前安装mysql情况&#xff0c;查找以前是否装有mysql rpm -qa|grep -i mysql这里显示我安装的 MySQL 服务有有&#xff1a; 2. 停止 mysql 服务、删除之前安装的 mysql 删除命令&#xff1a;rpm -e –n…

Unity进阶–通过PhotonServer实现人物选择和多人同步–PhotonServer(四)

文章目录 Unity进阶–通过PhotonServer实现人物选择和多人同步–PhotonServer(四)服务端客户端 Unity进阶–通过PhotonServer实现人物选择和多人同步–PhotonServer(四) 服务端 服务端结构如下&#xff1a; UserModel using System; using System.Collections.Generic; usin…

学习笔记|基于Delay实现的LED闪烁|u16是什么|a--和--a的区别|STC32G单片机视频开发教程(冲哥)|第六集(上):实现LED闪烁

文章目录 摘要软件更新什么是闪烁Tips:u16是什么? 语法分析&#xff1a;验证代码Tips&#xff1a;a--和--a的区别&#xff08;--ms 的用法&#xff09;测试代码&#xff1a; 摘要 1.基于Delay实现的LED闪烁 2.函数的使用 3,新建文件&#xff0c;使用模块化编程 软件更新 打…

微信ipad协议

前言 微信协议就是基于微信IPad协议的智能控制系统&#xff0c;利用人工智能AI技术、云计算技术、虚拟技术、边缘计算技术、大数据技术&#xff0c; 打造出智能桌面系统RDS、 智能聊天系统ACS 、智能插 件系统PLUGIN 、云计算服务CCS 、 任务管理系统TM、设备管理服务DM、 应…

分布式链路追踪——Dapper, a Large-Scale Distributed Systems Tracing Infrastructure

要解决的问题 如何记录请求经过多个分布式服务的信息&#xff0c;以便分析问题所在&#xff1f;如何保证这些信息得到完整的追踪&#xff1f;如何尽可能不影响服务性能&#xff1f; 追踪 当用户请求到达前端A&#xff0c;将会发送rpc请求给中间层B、C&#xff1b;B可以立刻作…

HttpRunner接口自动化框架搭建,干货满满!

前言 除了前面讲述的unittest、pytest等等自动化框架外&#xff0c;HttpRunner也是当前流行自动化框架之一。 一、先简单来介绍下httprunner框架 1、它是面向HTTP协议的测试框架&#xff0c;独立于语言之外&#xff0c;无需编写代码脚本&#xff0c;只需要去维护yaml/json文…

Windows安装Go开发环境

Windows安装Go开发环境 一、Go语言下载地址 https://golang.google.cn/dl/ 二、设置工作空间GOPATH目录(Go语言开发的项目路径) 首先进入我的C盘&#xff08;你放到其他盘也行&#xff09;&#xff0c;新建一个文件夹&#xff0c;名字叫做mygo&#xff08;这个就是你的工作目…

通讯录的实现

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 望小伙伴们点赞&#x1f44d;收藏✨加关注哟&#x1f495;&#x1…

LED电子显示屏信号传输方式

LED电子显示屏广泛应用在休闲文化广场、繁华商贸中心、商业街、火车站、地铁、商场、等场所。由于其应用领域的多样性、复杂性&#xff0c;对音视频信号传输的稳定、可靠、流畅性也提出了更高的要求。单屏播放&#xff0c;亦或是组网联播等场合下全彩LED显示屏的信号采用的传输…

独立站SEO是什么意思?自主网站SEO的含义?

什么是独立站SEO优化&#xff1f;自建站搜索引擎优化是指什么&#xff1f; 独立站SEO&#xff0c;作为网络营销的重要一环&#xff0c;正在逐渐引起人们的关注。在当今数字化时代&#xff0c;独立站已经成为许多企业、个人宣传推广的首选平台之一。那么&#xff0c;究竟什么是…

C++对象模型实验(clang虚函数表结构)

摘要&#xff1a;本科期间有对比过msvc&#xff0c;gcc&#xff0c;clang的内存布局&#xff0c;距今已经6-7年了&#xff0c;当时还是使用的c11。时间过得比较久了&#xff0c;这部分内容特别是内存对齐似乎C17发生了一些变化&#xff0c;因此再实践下C类模型。本文描述了C不同…

docker优点简介和yum方式安装

一.docker简介 二.docker的优点 1.交付和部署速度快 2.高效虚拟化 3.迁移性和扩展性强 4.管理简单 三.docker的基本概念 1.镜像 2.容器 3.仓库 四.docker的安装部署 &#xff08;1&#xff09;点击容器 ​&#xff08;2&#xff09;选择docker-ce&#xff0c;根据相…

[C语言]分支语句和循环语句

[C语言]分支语句和循环语句 文章目录 [C语言]分支语句和循环语句C语言语句分类分支语句if语法结构else的匹配规则switch语句switch语句中的breakswitch语句中default 循环语句while循环while循环中的break和continuefor循环for循环中的break和continuefor循环的变种do while循环…

【腾讯云Cloud Studio实战训练营】用Vue+Vite快速构建完成交互式3D小故事

&#x1f440;前置了解&#xff1a;(官网 https://cloudstudio.net/) 什么是Cloud Studio&#xff1f; Cloud Studio 是基于浏览器的集成式开发环境&#xff08;IDE&#xff09;&#xff0c;为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装&#…

refresh大揽

注意在每一步大操作之前都有一个前期准备 prepareRefresh&#xff08;&#xff09; 设置spring启动的时间 设置spring关闭和开启的标志位 获取环境对象&#xff0c;并设置一些属性值&#xff0c;是系统环境的不是xml设置的 设置监听器&#xff0c;以及需要发布事件的集合。 Con…

浅析Java设计模式之四策略模式

title: 浅析Java设计模式之四策略模式 date: 2018-12-29 17:26:17 categories: 设计模式 description: 浅析Java设计模式之四策略模式 1. 目录 1. 目录2. 概念 2.1. 应用场景2.2. 优缺点 2.2.1. 优点2.2.2. 缺点 3. 模式结构4. 样例 4.1. 定义策略4.2. 定义具体策略4.3. 定义…

【自动电压调节器】无功功率控制的终端电压控制研究(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…