【MyBatis源码】SqlSource对象创建流程

文章目录

    • 介绍
    • XMLScriptBuilder初始化
    • parseDynamicTags解析动态节点
    • RawSqlSource分析
      • 代码分析
      • 实例化

介绍

代码入口:

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

languageRegistry:用于注册LanguageDriver,LanguageDriver用于解析SQL配置,将配置信息转换为SqlSource对象。

MyBatis中的SqlSource用于描述SQL资源,MyBatis可以通过两种方式配置SQL信息,一种是通过@Selelect、@Insert、@Delete、@Update或者@SelectProvider、@InsertProvider、@DeleteProvider、@UpdateProvider等注解;另一种是通过XML配置文件。SqlSource就代表Java注解或者XML文件配置的SQL资源。

public interface SqlSource {BoundSql getBoundSql(Object parameterObject);}

SqlSource接口的定义非常简单,只有一个getBoundSql()方法,该方法返回一个BoundSql实例。BoundSql是对SQL语句及参数信息的封装,它是SqlSource解析后的结果。如图9-1所示,SqlSource接口有4个不同的实现,分别为StaticSqlSource、DynamicSqlSource、RawSqlSource和ProviderSqlSource。

这4种SqlSource实现类的作用如下。
ProviderSqlSource:用于描述通过@Select、@SelectProvider等注解配置的SQL资源信息。DynamicSqlSource:用于描述Mapper XML文件中配置的SQL资源信息,这些SQL通常包含动态SQL配置或者${}参数占位符,需要在Mapper调用时才能确定具体的SQL语句。
RawSqlSource:用于描述Mapper XML文件中配置的SQL资源信息,与DynamicSqlSource不同的是,这些SQL语句在解析XML配置的时候就能确定,即不包含动态SQL相关配置。
StaticSqlSource:用于描述ProviderSqlSource、DynamicSqlSource及RawSqlSource解析后得到的静态SQL资源。

XMLScriptBuilder初始化

  @Overridepublic SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);return builder.parseScriptNode();}

在 MyBatis 源码中,initNodeHandlerMap 方法的作用是初始化一个用于处理不同节点类型的映射表(nodeHandlerMap)。每个节点类型(例如 “trim”、“where”、“set” 等)对应一个处理器(例如 TrimHandler、WhereHandler 等)。这些处理器负责解析和处理相应的 SQL 语句节点。

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

• trim:用于去除 SQL 语句两端的空格或特定字符。
• where:用于生成 WHERE 子句。
• set:用于生成 SET 子句,通常在更新操作中使用。
• foreach:用于处理集合中的元素,通常用于生成批量插入或更新的 SQL。
• if:用于根据条件动态生成 SQL 片段。
• choose、when、otherwise:类似于 Java 的 switch-case 语句,用于动态选择生成不同的 SQL 片段。

parseDynamicTags解析动态节点

  protected MixedSqlNode parseDynamicTags(XNode node) {List<SqlNode> contents = new ArrayList<>();NodeList children = node.getNode().getChildNodes();for (int i = 0; i < children.getLength(); i++) {XNode child = node.newXNode(children.item(i));// 如果子节点是文本节点或 CDATA 节点,提取其文本内容并创建 TextSqlNode 对象if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE|| child.getNode().getNodeType() == Node.TEXT_NODE) {String data = child.getStringBody("");TextSqlNode textSqlNode = new TextSqlNode(data);// 如果是动态的,添加到 contents 列表中,并标记为动态;否则,创建一个静态文本节点并添加if (textSqlNode.isDynamic()) {// 检查这个文本节点是否为动态,即是否包含 ${}动态表达式contents.add(textSqlNode);isDynamic = true;} else {// 将带有#{}符号的SQL封装到StaticTextSqlNode节点上contents.add(new StaticTextSqlNode(data));}} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {// 如果子节点是元素节点,获取节点名称并查找对应的处理器(NodeHandler)String nodeName = child.getNode().getNodeName();NodeHandler handler = nodeHandlerMap.get(nodeName);if (handler == null) {throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");}handler.handleNode(child, contents);isDynamic = true;}}// 将所有解析的 SQL 节点封装到 MixedSqlNode 对象中并返回return new MixedSqlNode(contents);}

以下面这个SQL语句作为范例进行描述解析

    select * from t_user where id = #{id}<choose><when test="name != null">AND name like #{name}</when><otherwise>AND name = #{name}</otherwise></choose>

select * from t_user where id = #{id} 文本走的是Node.TEXT_NODE
而对于 都属于Node.ELEMENT_NODE
如果子节点是元素节点,获取节点名称并查找对应的处理器(NodeHandler)

   // 如果子节点是元素节点,获取节点名称并查找对应的处理器(NodeHandler)String nodeName = child.getNode().getNodeName();// 获取对应的节点处理器NodeHandler handler = nodeHandlerMap.get(nodeName);if (handler == null) {throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");}// 执行具体的节点处理器解析方法handler.handleNode(child, contents);isDynamic = true;

解析完各个Node节点后,封装SqlSource

  public SqlSource parseScriptNode() {// 解析动态 SQL 标签,并生成一个 MixedSqlNode 对象MixedSqlNode rootSqlNode = parseDynamicTags(context);SqlSource sqlSource;if (isDynamic) {// 如果是包含${}符号或者包含动态SQL的元素节点sqlSource = new DynamicSqlSource(configuration, rootSqlNode);} else {// 纯文本SQL,仅包含#{}sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);}return sqlSource;}

RawSqlSource分析

代码分析

RawSqlSource 的作用是将没有动态 SQL 标签的 SQL 语句(例如 、 等)直接解析成静态 SQL,避免了动态解析的开销。它会将 SQL 中的 #{} 占位符参数解析成 StaticSqlSource,然后存储为 BoundSql 对象,以便在执行时直接使用。

  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {// 解析SQL语句SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);// 获取入参类型Class<?> clazz = parameterType == null ? Object.class : parameterType;//解析SQL语句sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());}

下面这段代码是 SqlSourceParser 的 parse 方法,它负责将包含 #{} 占位符的 SQL 字符串解析为 StaticSqlSource 对象。StaticSqlSource 表示一个纯静态的 SQL 源,适合不含动态 SQL 标签的场景。以下是对这段源码的详细分析

 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;if (configuration.isShrinkWhitespacesInSql()) {sql = parser.parse(removeExtraWhitespaces(originalSql));} else {sql = parser.parse(originalSql);}return new StaticSqlSource(configuration, sql, handler.getParameterMappings());}

ParameterMappingTokenHandler 是 MyBatis 中一个用于处理 #{} 占位符的处理器。它会根据 parameterType 和 additionalParameters 等信息,将 #{} 占位符替换为相应的 JDBC 参数标记(如 ?),并生成对应的 ParameterMapping 列表,用于描述每个参数的类型和属性。

GenericTokenParser 是 MyBatis 中一个通用的占位符解析器,它能够识别带有指定前后缀的占位符。这里的 parser 会针对 #{} 占位符进行解析,将匹配到的 #{} 内的内容交给 handler 处理。

通过 GenericTokenParser 对 SQL 字符串进行解析,处理 #{} 占位符。
removeExtraWhitespaces(originalSql) 方法会去除多余的空白字符,确保 SQL 语句结构更简洁。
最终的 sql 字符串中,#{} 占位符将被替换为 ?,以便后续参数绑定

解析完成后,parse 方法会返回一个 StaticSqlSource 对象。StaticSqlSource 是 MyBatis 中用于表示静态 SQL 的 SqlSource 实现,适合不包含动态逻辑的 SQL。handler.getParameterMappings() 获取所有参数的映射信息,以便在执行时能够将参数正确绑定到 SQL 的 ?占位符上。

实例化

示例SQL:

     select * from t_user where id = #{id} name = #{name}

parse 方法的整体流程是:解析 #{} 占位符,将其替换为 ?,并记录参数映射信息。

经过 parse 处理后,原始 SQL 将被替换为:select * from t_user where id = ? name = ?
在这里插入图片描述
handler.getParameterMappings() 的作用是返回一个 ParameterMapping 列表,用于描述 SQL 中的 #{} 占位符参数的映射信息。ParameterMapping 对象包含了每个参数的名称、类型、Java 属性等,这些信息在 SQL 执行时用于将实际参数绑定到 ? 占位符上。
在这里插入图片描述
ParameterMappingTokenHandler 会生成一个 ParameterMapping 列表,包含两个参数的映射信息。
在 SQL 执行时,MyBatis 会使用 parameterMappings 列表来将 User 对象中的 id 和 name 属性值绑定到 SQL 中的 ? 占位符上。
ParameterMappingTokenHandler 在解析 #{} 占位符时,会将每个参数按出现的顺序记录到 parameterMappings列表中。比如,#{id} 出现在 #{name} 之前,那么 parameterMappings 列表中,id 的映射会排在 name 之前。MyBatis 通过 parameterMappings 中记录的参数顺序、名称等信息,将参数依次正确绑定到 SQL 中的 ? 占位符上,因此在参数传递过程中能确保参数的正确替换位置。

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

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

相关文章

配合数据库进行网页的动态数据上传

1.接口文档编写 1.1 什么是接口文档 在项目开发中&#xff0c;web项目的前后端通常分离开发&#xff0c; 此过程中&#xff0c;需要由前后端工程师共同定义接口&#xff0c;编写接口文档&#xff0c; 之后大家都根据这个接口文档进行开发&#xff0c;到项目结束前都要一直维…

数据结构——基础知识补充

1.队列 1.普通队列 queue.Queue 是 Python 标准库 queue 模块中的一个类&#xff0c;适用于多线程环境。它实现了线程安全的 FIFO&#xff08;先进先出&#xff09;队列。 2.双端队列 双端队列&#xff08;Deque&#xff0c;Double-Ended Queue&#xff09;是一种具有队列和…

OpenSSL

OpenSSL 概述 OpenSSL 是一个开源的、安全传输协议实现工具&#xff0c;广泛应用于数据加密与解密、证书生成与管理以及其他安全性相关的任务。在现代网络安全中&#xff0c;OpenSSL 被用于构建和维护 SSL/TLS 通信&#xff0c;确保数据在传输过程中的机密性和完整性。 简单来…

「C/C++」C/C++预处理 之 X宏(X Macro)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

WPF+MVVM案例实战(四)- 自定义GroupBox边框样式实现

文章目录 1、项目准备2、功能实现1、EnviromentModel.cs 代码2、GroubBoxViewModel.cs 代码实现3、ViewModelLocator.cs 依赖注入4、GroubBoxWindow.xaml 样式布局5、数据绑定3、效果展示4、资源获取1、项目准备 打开项目 Wpf_Examples,新建 GroubBoxWindow.xaml 界面、Groub…

第十六章 Vue组件化开发及组件局部/全局注册

目录 一、组件化 1.1. 组件概述 1.2. 语法高亮插件 ​编辑 1.3. 组件内部组成 1.4. 让组件支持 less 1.5. 组件注册的两种方式 二、局部注册 2.1. 使用描述 2.2. 脚手架工程变动的核心代码 2.2.1. 工程结构图 2.2.2. App.vue 2.2.3. WzxHeader.vue 2.2.4. WzxMain…

excel斜线表头

检验数据验证对象 鼠标放在检验数据 验证对象中间&#xff0c;altenter 之后空格 选中格子&#xff0c;右键单元格格式&#xff0c; 完成 如果是需要多分割&#xff0c;操作一样&#xff0c;在画斜线的时候会有区别&#xff0c;在插入里面用直线画斜线即可 在表格插入的时…

【python】OpenCV—Connected Components

文章目录 1、任务描述2、代码实现3、完整代码4、结果展示5、涉及到的库函数6、参考 1、任务描述 基于 python opencv 的连通分量标记和分析函数&#xff0c;分割车牌中的数字、号码、分隔符 cv2.connectedComponentscv2.connectedComponentsWithStatscv2.connectedComponents…

日期选择简化版今日、本周、本月、本季度、本年

function 未来之窗_时间_现在() {let date new Date(),year date.getFullYear(), //获取完整的年份(4位)month date.getMonth() 1, //获取当前月份(0-11,0代表1月)strDate date.getDate() // 获取当前日(1-31),小时 date.getHours(),分钟 date.getMinutes();if (month &…

基于安卓Android的健康饮食系统APP(源码+文档+部署+讲解)

&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 会持续一直更新下去 有问必答 一键收藏关注不迷路 源码获取&#xff1a;https://pan.baidu.com/s/1aRpOv3f2sdtVYOogQjb8jg?pwdjf1d 提取码: jf1d &#…

【Unity基础】初识UI Toolkit - 编辑器UI

&#xff08;本文所需图片在文章上面的资源中&#xff0c;点击“立即下载”。&#xff09; 本文介绍了如何通过UI工具包&#xff08;UI Toolkit&#xff09;来创建一个编辑器UI。 一、创建项目 1. 打开Unity创建一个空项目&#xff08;任意模板&#xff09;&#xff0c;这里我…

【网络】传输层协议TCP

目录 四位首部长度 序号 捎带应答 标记位 超时重传机制 连接管理机制&#xff08;RST标记位&#xff09; 三次握手及四次挥手的原因 TCP的全称是传输控制协议&#xff08;Transmission Control Protocol&#xff09;&#xff0c;也就是说&#xff0c;对于放到TCP发送缓冲…

docker基础篇(尚硅谷)

学习链接 docker1️⃣基础篇&#xff08;零基小白&#xff09; - 语雀文档 (即本篇) Docker与微服务实战&#xff08;基础篇&#xff09; Docker与微服务实战&#xff08;高级篇&#xff09;- 【上】 Docker与微服务实战&#xff08;高级篇&#xff09;- 【下】 文章目录 学习…

Spark RDD

概念 RDD是一种抽象&#xff0c;是Spark对于分布式数据集的抽象&#xff0c;它用于囊括所有内存中和磁盘中的分布式数据实体 RDD 与 数组对比 对比项数组RDD概念类型数据结构实体数据模型抽象数据跨度单机进程内跨进程、跨计算节点数据构成数组元素数据分片(Partitions)数据…

OmicsTools软件和R语言分析环境安装配置答疑汇总最新版

OmicsTools软件和R语言分析环境安装配置答疑汇总 前言提示 我开发了一款本地电脑无限使用的零代码生信数据分析作图神器电脑软件OmicsTools&#xff0c;欢迎大家使用进行生物医学科研数据分析和作图&#xff0c;不需要学编程写代码&#xff0c;分析次数没有限制&#xff0c;可…

java web调试时清理当前网址的缓存

java web调试时清理当前网址的缓存 背景 开发后端接口的时候&#xff0c;出现页面已经重新部署启动。但页面报错404的问题。询问前端同学后&#xff0c;发现是因为没有清理页面缓存导致的。特别在此记录。 清理页面缓存 操作方式 chrome浏览器 F12 > 应用 > 存储 &g…

分布式 ID 生成策略(二)

在上一篇文章&#xff0c;分布式 ID 生成策略&#xff08;一&#xff09;&#xff0c;我们讨论了基于数据库的 ID 池策略&#xff0c;今天来看另一种实现&#xff0c;基于雪花算法的分布式 ID 生成策略。 如图所示&#xff0c;我们用 41 位时间戳 12 位机器 ID 10 位序列号&a…

解决edge浏览器无法同步问题

有时候电脑没带&#xff0c;但是浏览器没有同步很烦恼。chrome浏览器的同步很及时在多设备之间能很好使用。但是edge浏览器同步没反应。 在这里插入图片描述 解决方法&#xff1a; 一、进入edge浏览器点击图像会显示未同步。点击“管理个人资料”&#xff0c;进入后点击同步&…

【机器学习】14. 集成学习 ensemble: bagging, boosting, 随机森林 random forest

集成学习 ensemble: bagging, boosting, 随机森林 random forest 1. Ensemble 整体认知2. 使用Ensemble的原因3. 构建Ensemble 的方法4. Bagging&#xff08;bootstrap aggregation&#xff09;特点 5. BoostingAdaBoost整体算法思路 6. 比较7. 随机森林 1. Ensemble 整体认知 …

记录一次更新idea

一、官网下载安装包&#xff0c;拿所需版本 二、链接下载&#xff0c; 逐行仔细读readme.txt 三、执行script(unstall<->install)vbs、-javaagent:更改时记得