【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;// idprivate Integer fetchSize;// 每次从服务器获取的数量private Integer timeout;// 最长访问数据库的时间private StatementType statementType;// STATEMENT、PREPARED、CALLABLEprivate 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、SELECTprivate KeyGenerator keyGenerator;// 主键生成器private String[] keyProperties;private String[] keyColumns;private boolean hasNestedResultMaps;// 是否存在嵌套查询结果集private String databaseId;private Log statementLog;private LanguageDriver lang;// 语言驱动,永磊解析动态或静态SQLprivate String[] resultSets;// resultsetprivate 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对象// 这里的话默认是 XMLLanguageDriverString 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);// 去得到最后执行语句准备使用的语句类型,默认是 PreparedStatementStatementType 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>    
    @Testpublic 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 这种形式,全限定idid = 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 方法对 #{} 进行处理。(在上次博客中我自己也是理解的模模糊糊,这里明确后所以再指明一下)

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

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

相关文章

排序(七种排序)

1.插入排序 2.希尔排序 3.选择排序 4.堆排序 5.冒泡排序 6.快速排序 7.归并排序 1.插入排序 1.1思路 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为 止&#xff0c;得到一个新的有序序列 1.2实现 //插入排…

全志orangepi-zero2驱动编写

文章目录 编译内核拓展一下&#xff0c;如果是其他板子内核编译有几种方式&#xff08;可以不用看&#xff09;&#xff1a;以下是树莓派板子的内核编译 驱动编写框架驱动框架 结束 编译内核 编写驱动之前先去官网下载&#xff0c;手册&#xff0c;跳到5.几章先弄好内核编译 照…

openGauss学习笔记-45 openGauss 高级数据管理-物化视图

文章目录 openGauss学习笔记-45 openGauss 高级数据管理-物化视图45.1 全量物化视图45.1.1 全量物化视图语法格式45.1.2 全量物化视图参数说明45.1.3 全量物化视图示例 45.2 增量物化视图45.2.1 增量物化视图语法格式45.2.2 增量物化视图参数说明45.2.3 增量物化视图示例 openG…

工厂模式并不难理解

文章目录 工厂模式简单工厂模式简单工厂模式使用的场景 工厂方法模式工厂方法模式使用场景 抽象工厂模式抽象工厂模式使用场景 工厂模式 功能&#xff1a;将对象的创建交给工厂&#xff0c;我们只需要告诉工厂我们要什么对象就可以得到该对象。 目的&#xff1a;实现创建对象…

【算法刷题之链表篇(1)】

目录 1.leetcode-82. 删除排序链表中的重复元素 II&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;方法及思路&#xff08;一次遍历&#xff09;&#xff08;3&#xff09;代码实现 2.leetcode-19. 删除链表的倒数第 N 个结点&#xff08;1&#xff09;题目描述&a…

无需公网IP——搭建web站点

文章目录 概述使用 Raspberry Pi Imager 安装 Raspberry Pi OS设置 Apache Web 服务器测试 web 站点安装静态样例站点将web站点发布到公网安装 Cpolar内网穿透cpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配…

认识容器,走进Docker

文章目录 容器技术简介容器的核心技术容器平台技术容器的支持技术 Docker理念Docker安装配置阿里云镜像加速器 容器技术简介 一切在云端&#xff0c;万物皆容器&#xff0c;说到容器&#xff0c;大家都会想到Docker,Docker现在几乎是容器的代名词&#xff0c;什么是Docker&…

网络通信原理TCP的四次断开连接(第四十九课)

FIN:发端完成发送任务标识。用来释放一个连接。FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接。 SEQ:序号字段。 TCP链接中传输的数据流中每个字节都编上一个序号。序号字段的值指的是本报文段所发送的数据的第一个字节的序号。 序列号为X ACK :确认号 。 …

ViT模型架构和CNN区别

目录 Vision Transformer如何工作 ViT模型架构 ViT工作原理解析 步骤1&#xff1a;将图片转换成patches序列 步骤2&#xff1a;将patches铺平 步骤3&#xff1a;添加Position embedding 步骤4&#xff1a;添加class token 步骤5&#xff1a;输入Transformer Encoder 步…

【Rust】Rust学习 第十五章智能指针

指针 &#xff08;pointer&#xff09;是一个包含内存地址的变量的通用概念。这个地址引用&#xff0c;或 “指向”&#xff08;points at&#xff09;一些其他数据。Rust 中最常见的指针是第四章介绍的 引用&#xff08;reference&#xff09;。引用以 & 符号为标志并借用…

C# Linq源码分析之Take (三)

概要 本文在前两篇Take源码分析的基础上&#xff0c;着重分析Range参数中有倒数的情况&#xff0c;即分析TakeRangeFromEndIterator的源码实现。 源码及分析 TakeRangeFromEndIterator方法用于处理Range中的开始和结束索引存在倒数的情况。该方法位于Take.cs文件中。通过yie…

Lnton羚通算法算力云平台在环境配置时 OpenCV 无法显示图像是什么原因?

问题&#xff1a; cv2.imshow 显示图像时报错&#xff0c;无法显示图像 0%| | 0/1 [00:00<…

构建 NodeJS 影院微服务并使用 docker 部署【01/4】

图片来自谷歌 — 封面由我制作 一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 在本系列中&#xff0c;我们将构建一个 NodeJS 微服务&#xff0c;并使用…

qt初入门0:结构体中QString用memset导致崩溃分析及QLatin1String简单查看源码

初识Qt,进行开发时遇到一个崩溃问题 简单整理 1&#xff1a;问题描述如下&#xff0c;结构体中QString成员&#xff0c;然后对结构体调用了memset导致问题&#xff1a; 2&#xff1a;问题分析&#xff0c;加断点调试的方式可以明确分析到行数 可以明确看出&#xff0c;初始化…

Windows下问题定位

1、内存相关知识点&#xff1b; 1&#xff09;windows下32位进程&#xff0c;用户态为2G内存&#xff0c;内核态也为2G内存&#xff1b;却别于linux操作系统&#xff1b; 备注&#xff1a;可以通过命令行与管理员权限&#xff0c;启动3G的用户态空间&#xff0c;但是部…

git权限问题解决方法Access denied fatal: Authentication failed

文章目录 遇到Access denied 的权限问题解决方法1、git的密码修改过&#xff0c;但是本地没更新。2、确定问题&#xff0c;然后增加配置① 查询用户信息②如果名称和email不对&#xff0c;设置名称&#xff1a;③ 检查ssh-add是否链接正常④ 设置不要每次都输入用户名密码 3、配…

【校招VIP】CSS校招考点之选择器优先级

考点介绍&#xff1a; 选择器是CSS的基础&#xff0c;也是校招中的高频考点&#xff0c;特别是复合选择器的执行优先级&#xff0c;同时也是实战中样式不生效的跟踪依据。 因为选择器的种类较多&#xff0c;很难直接记忆&#xff0c;可以考虑选择一个相对值&#xff0c;比如id类…

人大进仓数据库ksql命令基础

测试环境信息: 系统为银河麒麟V10 数据库为Kingbase ES V8 数据库安装目录为/opt/Kingbase/ES/V8 ksql命令位于/opt/Kingbase/ES/V8/Server/bin下 使用--help获取帮助 续上图 1.查看数据库列表 ./ksql -U system -l 2.查看数据库版本 ./ksql -V 3.连接指定的数据库tes…

【GAMES202】Real-Time Shadows2—实时阴影2

一、PCSS回顾 上一篇我们说了如何用PCSS是实现软阴影&#xff0c;这个过程是没有任何问题的&#xff0c;但是有一个速度的问题&#xff0c;因为PCSS涉及到非常多次对纹理特定某一块区域遍历的操作&#xff08;工业界一般都是用在该区域采样的方式&#xff0c;会因此产生噪声&am…

Zookeeper集群单节点启动成功但未同步其他节点数据

首先排查节点启动是否正常&#xff1a; 在zookeeper的bin目录下执行&#xff1a;sh zkServer.sh status 判断当前节点数据leader 还是follower 节点都启动正常&#xff0c;但某一个zookeeper集群节点&#xff08;下面简称“异常节点”&#xff09;不同步其他节点数据&#xf…