MyBatis 学习笔记(八)---源码分析篇--SQL 执行过程详细分析

2023-10-31

前言

在面试中我们经常会被到MyBatis中 #{} 占位符与${}占位符的区别。大多数的小伙伴都可以脱口而出#{} 会对值进行转义,防止SQL注入。而${}则会原样输出传入值,不会对传入值做任何处理。本文将通过源码层面分析为啥#{} 可以防止SQL注入。

源码解析

首先我们来看看MyBatis 中SQL的解析过程,MyBatis 会将映射文件中的SQL拆分成一个个SQL分片段,然后在将这些分片段拼接起来。
例如:在映射文件中有如下SQL

 SELECT * FROM student
        <where>
            <if test="id!=null">
                id=${id}
            </if>
            <if test="name!=null">
                AND name =${name}
            </if>
        </where>

MyBatis 会将该SQL 拆分成如下几部分进行解析
第一部分 SELECT * FROM Author 由StaticTextSqlNode存储
第二部分 <where> 由WhereSqlNode 存储
第三部分 <if></if> 由IfSqlNode存储
第四部分 ${id} ${name} 占位符里的文本由TextSqlNode存储。

获取BoundSql

BoundSql 是用来存储一个完整的SQL 语句,存储参数映射列表以及运行时参数

public class BoundSql {

  /**
   * 一个完整的SQL语句,可能会包含问号?占位符
   */
  private String sql;
  /**
   * 参数映射列表,SQL中的每个#{xxx}
   * 占位符都会被解析成相应的ParameterMapping对象
   */
  private List<ParameterMapping> parameterMappings;
  /**
   * 运行时参数,即用户传入的参数,比如Article对象,
   * 或是其他的参数
   */
  private Object parameterObject;
    /**
   * 附加参数集合,用户存储一些额外的信息,比如databaseId等
   */
  private Map<String, Object> additionalParameters;
  /**
   * additionalParameters的元信息对象
   */
  private MetaObject metaParameters;
    .... 省略部分代码
  }

分析SQL的解析,首先从获取BoundSql说起。其代码源头在MappedStatement。

  public BoundSql getBoundSql(Object parameterObject) {
	//其实就是调用sqlSource.getBoundSql
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //剩下的可以暂时忽略,故省略代码
    return boundSql;
  }

如上,可以看出其内部就是调用的sqlSource.getBoundSql。 而我们sqlSource 接口又有如下几个实现类。
DynamicSqlSource
RawSqlSource
StaticSqlSource
ProviderSqlSource
VelocitySqlSource
其中DynamicSqlSource 是对动态SQL进行解析,当SQL配置中包含${}或者<if>,<set> 等标签时,会被认定为是动态SQL,此时使用 DynamicSqlSource 存储 SQL 片段,而RawSqlSource 是对原始的SQL 进行解析,而StaticSqlSource 是对静态SQL进行解析。这里我们重点介绍下DynamicSqlSource。话不多说,直接看源码。

  public BoundSql getBoundSql(Object parameterObject) {
    //生成一个动态上下文
    DynamicContext context = new DynamicContext(configuration, parameterObject);
	//这里SqlNode.apply只是将${}这种参数替换掉,并没有替换#{}这种参数
    rootSqlNode.apply(context);
	//调用SqlSourceBuilder
    SqlSourceBuilder sqlSourceParser =  new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
	//SqlSourceBuilder.parse,注意这里返回的是StaticSqlSource,解析完了就把那些参数都替换成?了,也就是最基本的JDBC的SQL写法
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
	//看似是又去递归调用SqlSource.getBoundSql,其实因为是StaticSqlSource,所以没问题,不是递归调用
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
//  将DynamicContext的ContextMap中的内容拷贝到BoundSql中
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

如上,该方法主要有如下几个过程:

  1. 生成一个动态上下文
  2. 解析SQL片段,替换${}类型的参数
  3. 解析SQL语句,并将参数都替换成?
  4. 调用StaticSqlSource的getBoundSql获取BoundSql
  5. 将DynamicContext的ContextMap中的内容拷贝到BoundSql中。
    下面通过两个单元测试用例理解下。
  @Test
  public void shouldMapNullStringsToNotEmptyStrings() {
    final String expected = "id=${id}";
    final MixedSqlNode sqlNode = mixedContents(new TextSqlNode(expected));
    final DynamicSqlSource source = new DynamicSqlSource(new Configuration(), sqlNode);
    String sql = source.getBoundSql(new Bean("12")).getSql();
    Assert.assertEquals("id=12", sql);
  }
 
    @Test
  public void shouldMapNullStringsToJINHAOEmptyStrings() {
    final String expected = "id=#{id}";
    final MixedSqlNode sqlNode = mixedContents(new TextSqlNode(expected));
    final DynamicSqlSource source = new DynamicSqlSource(new Configuration(), sqlNode);
    String sql = source.getBoundSql(new Bean("12")).getSql();
    Assert.assertEquals("id=?", sql);
  }

如上,${} 占位符经过DynamicSqlSource的getBoundSql 方法之后直接替换成立用户传入值,而#{} 占位符则仅仅只是只会被替换成?号,不会被设值。

DynamicContext

DynamicContext 是SQL语句的上下文,每个SQL片段解析完成之后会存入DynamicContext中。让我们来看看DynamicContext的相关代码。

。。。。。。。。。。。。。。。。。

版权原因,完整文章,请参考如下:MyBatis 学习笔记(八)---源码分析篇--SQL 执行过程详细分析

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MyBatis 学习笔记(八)---源码分析篇--SQL 执行过程详细分析 的相关文章

  • 使用每个单独行的多个“where”子句更新多行

    我正在尝试像这样更新我的表 Update MyTable SET value 1 WHERE game id 1 x 4 y 8 SET value 2 WHERE game id 1 x 3 y 7 SET value 3 WHERE g
  • 使用临时表替换 WHERE IN 子句

    我让用户输入我需要在表中查询的值列表 该列表可能非常大 并且长度在编译时未知 而不是使用WHERE IN 我认为使用临时表并对其执行联接会更有效 我在另一个SO问题中读到了这个建议 目前找不到它 但会在找到时进行编辑 要点是这样的 CREA
  • SQL Server 2008 R2 DMV - sys.dm_sql_referencing_entities - 查询用法

    我正在尝试使用以下命令获取表列表的所有依赖项sys dm sql referencing entities DMV 这个查询给了我所有表的列表 SELECT TableName from FinalTableList 此查询给出 Table
  • ORACLE 在立即执行中批处理 DDL 语句

    我正在尝试在一个 Execute Immediate 语句中运行多个 ddl 语句 我认为这会很简单 但看来我错了 想法是这样的 declare v cnt number begin select count into v cnt from
  • MySQL如何进行浮点加法的数学计算?

    我测试过SELECT 0 1 0 2 用MySQL MariaDB 查询 它返回了正确的答案 MariaDB none gt SELECT 0 1 0 2 0 1 0 2 0 3 1 row in set 0 000 sec 在大多数编程语
  • Postgresql 和 jsonb - 将键/值插入多级数组

    非常类似于这个帖子 https stackoverflow com questions 58959678 postgresql add key to each objects of an jsonb array 但我很难适应他们的解决方案
  • T-SQL 相当于 =rand()

    我有几个内容表 我想用随机的文本段落填充它们 在 MS Word 中 我只需输入 rand 即可 我收到三段新鲜的文字 是否有 SQL 脚本 命令可用于使用 t sql 生成随机字典单词 declare Lorem nvarchar max
  • 如何防止用户生成的 Sql 查询上的 Sql 注入

    我有一个项目 私有的 ASP net 网站 受 https 密码保护 其中要求之一是用户能够输入直接查询数据库的 Sql 查询 我需要能够允许这些查询 同时防止它们对数据库本身造成损坏 以及访问或更新它们不应该访问 更新的数据 我制定了以下
  • 如何在存储过程中使用名称求和和分组?

    我想对钱列求和 但我想要状态中的组名称和代码 这是存储过程代码 Sql Server 2008 SELECT um upmoney as money um pId as code um FName as name up status as
  • SQL FORMAT 函数错误

    这个SQL select FORMAT lNum from rpt myView 产生以下错误 参数数据类型 varchar 对于格式的参数 1 无效 功能 lNum is a varchar 10 运行 SQL Server 2012 v
  • MySQL 中有“connect by”替代方案吗?

    如果我使用 Oracle 有connect by可用于创建分层查询的关键字 目前我正在一个项目中使用MySQL 我想知道是否有替代方案connect by在 MySQL 中 我尝试过谷歌 但到目前为止还没有结果 我想要实现的是通过一个查询从
  • 方法“Boolean Contains(System.String)”不支持对 SQL 的转换

    方法 Boolean Contains System String 不支持对 SQL 的转换 查询是 IsQueryable 但这停止工作 foreach string s in collection1 if s Length gt 0 q
  • 从 CTE 插入

    WITH DTL AS SELECT CMPI CODE CMN CDTY MTRL CMI WT FACTOR CMI CNTRCT RATE PL PRESENT PRICE TRM CODE ROUND NVL PRESENT PRI
  • ADO.NET 池连接无法重用

    我正在开发一个 ASP NET MVC 应用程序 该应用程序使用 EF 6 x 来处理我的 Azure SDL 数据库 最近 随着负载的增加 应用程序开始进入无法再与 SQL 服务器通信的状态 我可以看到有 100 个到我的数据库的活动连接
  • 删除或更改 ETL 中的记录

    我有一个表 我在上面构建了 ETL 服务 货物记录 到达 离开 进入表格 我已经这样做了 我的桌子将被删除 当项目标识符第二次到达数据库时 两条记录都被删除 label cost time x2 29 14 5 2020 01 00 00
  • JbdcTemplate - 带有动态 SQL 查询的PreparedStatements

    I know jdbcTemplate可以用来创建PreparedStatements如果你这样设置 i e private JdbcTemplate jdbcTemplate String sqlQuery Select from tab
  • C# 查询两个数据库的数据

    我目前有一个查询 我正在从两个不同的数据库获取数据 这些数据被附加到一个名为 accountbuys 的列表中 我的第一个表有三个数据条目 3个想要购买股票的帐户 下一张表有 17 个数据点 购买 17 只股票 I am merging t
  • SQL Like 带有子查询

    我怎样才能做到这一点 SELECT FROM item WHERE item name LIKE SELECT equipment type FROM equipment type GROUP BY equipment type 内部子查询
  • 如何显示 RSpec 测试生成的 SQL 查询日志?

    我正在为我的 Rails 3 应用程序编写规范 我想测试数据库事务是否真的有效 如果能够看到我的应用程序在规范驱动下生成的 sql 查询 这将非常有帮助 有没有办法像在 Rails 控制台中一样查看查询 我正在使用 Rails 3 0 9
  • SQL Server 标识列值从 0 而不是 1 开始

    我遇到了一个奇怪的情况 数据库中的某些表的 ID 从 0 开始 即使 TABLE CREATE 的 IDENTITY 1 1 也是如此 对于某些表来说是这样 但对于其他表则不然 它一直有效到今天 我尝试过重置身份列 DBCC CHECKID

随机推荐

  • DBSCAN聚类算法的Python可视化

    DBSCAN全称为 Density Based Spatial Clustering of Applications with Noise 我们可以利用sklearn在python中实现DBSCAN 首先 import相关的Library
  • 服务器温度显示,服务器温度查看

    服务器温度查看 内容精选 换一换 华为云帮助中心 为用户提供产品简介 价格说明 购买指南 用户指南 API参考 最佳实践 常见问题 视频帮助等技术文档 帮助您快速上手使用华为云服务 设备实时状态查询是检测设备在运行过程中的状态信息 用户可任
  • java 范围注解_Java-----注解

    1 什么是注解 Annotation是JDK5 0开始引入的新技术 Annotation的作用 不是程序本身 可以对程序作出解释 这一点 跟注释没什么区别 可以被其他程序 比如 编译器 读取 注解信息处理流程 是注解和注释的重大区别 Ann
  • Matlab矩阵基本操作(定义,运算)

    关注公众号 coding进阶 获取更多实战技术 一 矩阵的表示 在MATLAB中创建矩阵有以下规则 a 矩阵元素必须在 内 b 矩阵的同行元素之间用空格 或 隔开 c 矩阵的行与行之间用 或回车符 隔开 d 矩阵的元素可以是数值 变量 表达
  • Anaconda安装-超详细版(2023)

    Anaconda安装 超详细版 2023 前言 彻底卸载python Anaconda下载地址 安装详细步骤 配置环境变量 检验安装是否成功 更改conda源 后续安装第三方库可以加快速度 超详细彻底卸载Anaconda教程 Tensorf
  • 交互设计实用指南系列11-减少记忆负担

    http ued taobao org blog 2010 03 交互设计实用指南系列11 减少记忆负担 科普 辞海 中 记忆 的定义是 人脑对经验过的事物的识记 保持 再现或再认 记忆是人类一个非常重要的心理活动 它是人类很多其他思维活动
  • 【HJ72】百钱买百鸡问题

    题目描述 公元前五世纪 我国古代数学家张丘建在 算经 一书中提出了 百鸡问题 鸡翁一值钱五 鸡母一值钱三 鸡雏三值钱一 百钱买百鸡 问鸡翁 鸡母 鸡雏各几何 现要求你打印出所有花一百元买一百只鸡的方式 输入描述 输入任何一个整数 即可运行程
  • 从浏览器地址栏输入url到显示页面的步骤

    浏览器输入URL到渲染过程解析 从浏览器地址栏输入url到显示页面的步骤 从浏览器地址栏输入url到显示页面的步骤 目录 1 概述 2 DNS域名解析 3 三次握手与四次挥手 4 DOM CSSOM 渲染树的构建 渲染与绘制 1 概述 浏览
  • Mac里的airdrop传输文件

    01 两方打开 02 设置对所有人可以发现 03 右键需要传输的文件 04 选择共享 airdrop 05 点击页面上的名称 开始传输 note 可以传输 app文件 这样可以大大节省下载安装所要耗费的时间 word只要一分钟就能从从无到有
  • 机器学习_深度学习毕设题目汇总——图像生成

    下面是该类的一些题目 题目 基于生成式对抗网络的无监督图像生成研究 用于图像生成的机器学习算法在人像合成中的研究与应用 可抵抗相机抖动的高动态图像生成算法研究 图像生成诗歌的端到端模型研究 基于GANs的脑部MRI图像生成方法研究 基于GA
  • 问二十四:分类说明JUC包常用类有哪些?

    JUC中常用类汇总 JUC的atomic包下运用了CAS的AtomicBoolean AtomicInteger AtomicReference等原子变量类 JUC的locks包下的AbstractQueuedSynchronizer AQ
  • 【Ensemble Learning】第 3 章:混合模型

    在第2章中 您学习了如何以不同方式划分和混合训练数据以构建集成模型 其性能优于在未划分数据集上训练的模型 在本章中 您将学习不同的组装方法 与混合训练数据方法不同 混合模型方法在不同的机器学习模型中使用相同的数据集 然后以不同的方式组合结果
  • pads win10显示不全

    打开注册表 WIN R 1 路径 HKEY LOCAL MACHINE SOFTWARE Microsoft Windows NT CurrentVersion Fonts 将原来的 Microsoft YaHei TrueType Mic
  • C++ Vector容器的push_back( )与pop_back( )函数

    C Vector容器的push back 与pop back 函数 push back pop back 参考链接 push back 函数将一个新的元素加到vector的最后面 位置为当前最后一个元素的下一个元素 push back 在V
  • java IDEA加密/解密 源码

    package tool import org apache commons codec binary Base64 import org bouncycastle jce provider BouncyCastleProvider imp
  • Git如何Check Out出指定文件或者文件夹

    原文 http www handaoliang com a 20140506 195406 html 在进行项目开发的时候 有时候会有这样的需求那就是 我们只希望从Git仓库里取指定的文件或者文件夹出来 在SVN里面 这非常容易实现 因为S
  • 实训9——门磁报警

    实验九 门磁报警 一 实验目的 通过门磁传感器 判断三种开门方式 1 正常开门 就是有正常开门方式 例指纹开门 蓝牙开门后 主人打开门 触发门磁 2 门未关好 在正常开门后 主人没有即使关门 会引发门未关好报警 3 有人撬门 非正常开门 即
  • [Python人工智能] 十五.无监督学习Autoencoder原理及聚类可视化案例详解

    从本专栏开始 作者正式研究Python深度学习 神经网络及人工智能相关知识 前一篇文章详细讲解了循环神经网络LSTM RNN如何实现回归预测 通过sin曲线拟合实现如下图所示效果 本篇文章将分享无监督学习Autoencoder的原理知识 然
  • 用户增长漫谈二

    除了功能迭代 增量开发配套敏捷理念 这里重要谈一个概念 宏观 微观上都有可取之处 品牌架构 反应企业意愿 个性 未来可能性留存 兼容不同利益群体方式进程商业活动 主品牌 标识性弱 但是个性强烈 业务 产品特性底的处理方式 以资金 实力彰显
  • MyBatis 学习笔记(八)---源码分析篇--SQL 执行过程详细分析

    前言 在面试中我们经常会被到MyBatis中 占位符与 占位符的区别 大多数的小伙伴都可以脱口而出 会对值进行转义 防止SQL注入 而 则会原样输出传入值 不会对传入值做任何处理 本文将通过源码层面分析为啥 可以防止SQL注入 源码解析 首