当前位置: 首页 > news >正文

简单学习一下 MyBatis 动态SQL使用及原理

MyBatis 是一个优秀的持久层框架,它提供了丰富的 SQL 映射功能,可以让我们通过 XML 或注解方式来定义 SQL 语句。它很大程度上简化了数据库操作,提高了开发效率。动态 SQL 是其中一个非常重要的功能,可以让我们根据不同的条件动态生成 SQL 语句,提高了 SQL 的灵活性和可重用性。本文将详细介绍 MyBatis 的动态 SQL 使用与原理。

1. 动态SQL概述

动态SQL是指根据条件拼接SQL语句的功能,可以在SQL语句中添加或者删除某些条件和语句。在实际开发中,我们经常需要根据不同的条件拼接不同的SQL语句。如果只使用静态SQL,会使得代码冗余度高、可读性差、维护成本高等问题。而使用动态SQL可以很好地解决这些问题。

MyBatis中提供了很多种方式来实现动态SQL,包括if、choose、when、otherwise、trim、where、set等。

2. if标签

if标签是MyBatis中最常用的动态SQL标签之一。它通常用来判断条件是否成立,从而确定是否加入SQL语句中。下面是一段示例代码:

xml复制代码<select id="selectUsers" resultMap="UserResultMap">SELECT * FROM Users <where><if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></where>
</select>

上述代码中,通过if标签的test属性来判断条件是否成立。只有当"name"和"age"都不为空时,才会将其加入到SQL语句中。这样就可以在不同的情况下生成不同的SQL语句。

3. choose、when和otherwise标签

choose、when和otherwise标签通常一起使用,它类似于Java中的switch语句。下面是一段示例代码:

xml复制代码<select id="selectUsers" resultMap="UserResultMap">SELECT * FROM Users <where><choose><when test="name != null">AND name = #{name}</when><when test="age != null">AND age = #{age}</when><otherwise>AND id > 0</otherwise></choose></where>
</select>

choose、when和otherwise标签中,如果test条件成立,就会将当前标签中的SQL语句加入到最终的SQL语句中。只有一个可以成立,多个成立时按顺序第一个生效。

4. trim标签

trim标签通常用来去掉特定字符或者关键字。下面是一段示例代码:

xml复制代码<select id="selectUsers" resultMap="UserResultMap">SELECT * FROM Users <where><trim prefix="AND" prefixOverrides="OR"><if test="name != null">OR name = #{name}</if><if test="age != null">OR age = #{age}</if></trim></where>
</select>

上述代码中,prefix属性表示在标签内部SQL语句前添加的字符;prefixOverrides属性表示从标签内部SQL语句开头去除的字符串。

5. set标签和where标签

set标签通常用来更新参数对象中的非空属性。where标签通常用来拼接SQL语句中的where条件。下面是一段示例代码:

xml复制代码<update id="updateUser" parameterType="User">UPDATE Users <set><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if></set><where>id = #{id}</where>
</update>

上述代码中,set标签用来设置要更新的字段,通过if标签判断哪些字段需要更新。where标签用来拼接SQL语句中的where条件,具体的条件可以根据实际情况进行调整。

6. foreach

foreach 标签用于处理集合类型的参数,比如 List、Array 等,可以遍历集合中的元素,将每个元素都转化为 SQL 语句的一部分,用于生成动态 SQL 语句。下面是一个示例:

xml复制代码<select id="getUserByIdList" resultType="User">SELECT * FROM userWHERE id IN<foreach collection="idList" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

在上述 SQL 语句中,我们通过 foreach 标签遍历传入的参数 idList,将其中的每个元素转化为一个 id,然后根据这些 id 拼接成一个 IN 子句。

7. bind

bind 标签用于定义一个变量,该变量可以被后续的 SQL 片段引用,方便了 SQL 的编写。下面是一个示例:

xml复制代码<select id="getUserByName" resultType="User"><bind name="queryName" value="'%' + name + '%'"/>SELECT * FROM user WHERE name like #{queryName}
</select>

在上述 SQL 语句中,我们使用 bind 标签定义了一个变量 queryName,它的值为 name 模糊查询的条件。然后使用该变量来拼接 SQL 语句,使得 SQL 语句更加简洁。

8. 动态SQL解析原理

MyBatis的动态SQL是通过OGNL表达式来实现的。OGNL(Object-Graph Navigation Language)是一种基于Java对象图遍历的表达式语言,它可以方便地访问Java对象的属性和方法。

在MyBatis中,通过OGNL表达式可以动态地计算条件是否成立,从而确定是否将SQL片段添加到最终的SQL语句中。OGNL表达式通常嵌入在MyBatis中的动态SQL标签中,例如if、choose、when、otherwise等。

MyBatis使用了两个重要的类来实现OGNL表达式的解析和计算:OgnlExpressionEvaluator和OgnlCache。OgnlExpressionEvaluator类负责将MyBatis传入的参数对象转换为OGNL表达式需要的上下文对象,然后将OGNL表达式计算结果返回;OgnlCache类负责缓存已经解析好的OGNL表达式,避免重复解析和计算。

具体的解析过程如下:

  1. 根据MyBatis的配置将Mapper.xml文件中的SQL语句解析为一个MappedStatement对象,并将其中的OGNL表达式解析成一个一个可执行的SQL片段。
  2. 对于每一个OGNL表达式,MyBatis使用${}来表示一个简单的OGNL表达式,使用#{}来表示一个OGNL表达式中包含复杂逻辑的情况。在解析过程中,MyBatis会将OGNL表达式中的参数进行解析和预处理,然后使用OgnlCache类将其缓存起来。
  3. 当Mapper接口方法被调用时,MyBatis会将方法中传入的参数对象转换为一个BoundSql对象,并将该BoundSql对象与MappedStatement对象一起传递给OgnlExpressionEvaluator类。
  4. OgnlExpressionEvaluator类中再次解析OGNL表达式,并将BoundSql对象作为上下文传入OGNL表达式中执行。OGNL表达式执行的结果将被转化为String类型,并返回给BoundSql对象。
  5. 最后,MyBatis将所有BoundSql对象中的SQL片段拼接成最终的SQL语句并执行。

MyBatis的动态SQL解析原理是将OGNL表达式解析为可执行的SQL片段,然后根据条件判断是否将该SQL片段加入到最终的SQL语句中。MyBatis使用OgnlExpressionEvaluator和OgnlCache类来实现OGNL表达式的解析和计算,从而实现动态SQL的功能。

在 MyBatis 的源码中,动态 SQL 还涉及到以下接口和类来实现:

  1. SqlNode 接口:表示一个 SQL 节点,也就是一个 SQL 片段。它包含一个 apply 方法,在执行 SQL 语句时会将 SQL 片段应用到相应的位置。
  2. MixedSqlNode 类:实现了 SqlNode 接口,可以包含多个子节点。该类的 apply 方法会依次遍历所有子节点,并将每个节点应用到 SQL 语句中。
  3. TextSqlNode 类:表示一个纯文本节点。该类包含一个文本字符串,可以将其直接应用到 SQL 语句中。
  4. IfSqlNode 类:表示一个条件节点。可以根据指定的条件判断是否需要应用该节点内部的 SQL 片段。如果条件成立,则会将 SQL 片段应用到 SQL 语句中。
  5. TrimSqlNode 类:表示一个修剪节点,可以根据配置对 SQL 片段进行修剪操作。常用于处理 UPDATE 和 INSERT 语句中 SET 子句的逗号问题。
  6. WhereSqlNode 类:表示一个 WHERE 条件节点。可以将 WHERE 子句的参数拼接到 SQL 语句中。

以上是 MyBatis 中实现动态 SQL 的核心接口和类。MyBatis 内部通过组合这些接口和类来构建复杂的 SQL 语句。通过定义这些接口和类,可以让开发者更加方便地书写动态 SQL 语句,并且遵循了设计模式中的单一职责原则。

还有一些Builder 接口及其实现类的作用都是用于构造 SQL 语句。下面简单介绍一下一些常用的 Builder 类型:

  1. BaseBuilder 接口:所有 Builder 的基础接口,定义了一些共同的方法,例如获取 Configuration 对象、创建 ParameterMapping 对象等。
  2. XMLMapperBuilder 类:从 XML 文件中解析出各种 SQL 节点,然后通过其他 Builder 对象将其转换成 SQL 语句。
  3. MapperBuilderAssistant 类:辅助 XMLMapperBuilder 类创建各种类型的 SQL 节点,例如创建 <select>、<update>、<insert> 等标签节点。
  4. SqlSourceBuilder 类:根据 XML 中的 SQL 片段创建 SqlSource 对象,SqlSource 对象中包含了解析后的 SQL 语句和参数信息。
  5. DynamicSqlSource 类:用于处理动态 SQL,也就是包含各种条件判断和循环语句的 SQL 片段。它是 SqlSource 接口的一种实现。
  6. StaticSqlSource 类:用于处理静态 SQL,即不包含任何条件语句和循环语句的 SQL 片段。它同样是 SqlSource 接口的一种实现。
  7. SqlSessionFactoryBuilder 类:用于创建 SqlSessionFactory 对象,它会将所有的 Builder 对象组合在一起,完成 SQL 语句的解析和构造。

通过上述不同类型的 Builder 对象,我们可以将 XML 中的 SQL 片段转换成 Java 对象,并且根据各种条件生成相应的 SQL 语句。这个过程中涉及到的类和方法非常多,需要我们深入地了解 MyBatis 的内部实现才能灵活运用。

9. 总结

本文通过介绍MyBatis动态SQL的基本概念和常用标签(if、choose、when、otherwise、trim、where、set、foreach),希望读者能够更加深入地了解MyBatis的使用和原理。在实际开发过程中,要根据具体场景和需求选择合适的动态SQL标签,从而实现灵活拼接SQL语句的功能,提高开发效率。

在 MyBatis 中,动态 SQL 主要包括以下几种类型:

  1. <if> 标签:表示一个条件语句,可以根据条件判断是否包含相应的 SQL 片段。
  2. <where> 标签:表示一个 WHERE 条件语句,可以根据配置自动添加 WHERE 关键字。
  3. <choose> 标签:表示一个选择语句,可以根据多个条件选择符合条件的 SQL 片段。
  4. <foreach> 标签:表示一个循环语句,在循环中动态生成 SQL 语句。
  5. <set> 标签:表示一个 SET 子句,可以根据指定的属性值动态生成 SET 语句。

以上标签都属于动态 SQL,在解析时需要通过特殊的方式进行处理。下面以 <if> 标签为例介绍解析原理:

  1. XMLScriptBuilder 类会根据标签类型创建相应的 SQL 节点,例如 <if> 标签对应的节点是 IfSqlNode 对象。
  2. XMLScriptBuilder 类会递归解析节点内部的子节点,并将其组合成一个 SQL 片段。
  3. 当解析到 IfSqlNode 节点时,XMLScriptBuilder 类会获取标签中的 test 属性,并根据该属性值创建一个 OgnlExpression 对象(OGNL 表达式对象),用于判断条件是否满足。
  4. 如果条件满足,则将子节点生成的 SQL 片段添加到当前 SQL 上下文中;否则忽略该节点。
  5. 最终生成的 SQL 语句就是将所有满足条件的 SQL 片段组合起来得到的。

以上就是 MyBatis 实现动态 SQL 解析的大体流程。通过 XMLScriptBuilder 类的递归解析,可以将各种类型的动态 SQL 节点转换成 SqlNode 接口的实现,然后通过 MixedSqlNode 类将它们组合成一个完整的 SQL 片段。

相关文章:

简单学习一下 MyBatis 动态SQL使用及原理

MyBatis 是一个优秀的持久层框架&#xff0c;它提供了丰富的 SQL 映射功能&#xff0c;可以让我们通过 XML 或注解方式来定义 SQL 语句。它很大程度上简化了数据库操作&#xff0c;提高了开发效率。动态 SQL 是其中一个非常重要的功能&#xff0c;可以让我们根据不同的条件动态…...

WhatsApp如何让客户参与变得更简单?

WhatsApp对你的品牌来说可能和Twitter和Facebook一样重要&#xff0c;你可能已经把它们纳入你的社交媒体战略。 是的&#xff0c;WhatsApp不仅仅可以用来给同事发短信或与远方的亲戚视频聊天&#xff0c;它也适用于商业。 在发展WhatsApp业务时&#xff0c;小企业主得到了最优…...

记一次 MySQL 主从同步异常的排查记录,百转千回

本文主要内容如下&#xff1a; 一、现象 最近项目的测试环境遇到一个主备同步的问题&#xff1a; 备库的同步线程停止了&#xff0c;无法同步主库的数据更改。 备库报错如下&#xff1a; 完整的错误信息&#xff1a; Relay log read failure: Could not parse relay log even…...

Cpython的多线程技术之痛

历史原因 在Python官网下载的默认解释器是采用C语言编写的Cpython解释器。在Python语言开发之初&#xff0c;计算机都是单核CPU&#xff0c;每个单核CPU同一时刻只能运行一个线程。为了模拟多线程工作&#xff0c;这里采用了模拟机制&#xff0c;让不同线程根据时间片段&#…...

NDK OpenGL离屏渲染与工程代码整合

NDK​系列之OpenGL离屏渲染与工程代码整合&#xff0c;本节主要是对上一节OpenGL渲染画面效果代码进行封装设计&#xff0c;将各种特效代码进行分离解耦&#xff0c;便于后期增加其他特效。 实现效果&#xff1a; 实现逻辑&#xff1a; 1.封装BaseFilter过滤器基类&#xff0c…...

Python基础入门编程代码练习(二)

一、求1~100之间不能被3整除的数之和 循环条件&#xff1a;i<100循环操作 实现代码如下&#xff1a; def sums():sum 0for num in range(1, 101):if num % 3 ! 0:sum numprint("1~100之间不能被3整除的数之和为&#xff1a;%s" % (sum))sums() print("1~…...

C# | 对象池

对象池 文章目录 对象池前言什么是对象池对象池的优点对象池的缺点 实现思路示例代码 结束语 前言 当我们开发一个系统或者应用程序时&#xff0c;我们通常需要创建很多的对象&#xff0c;这些对象可能是线程、内存、数据库连接、文件句柄等等。在某些情况下&#xff0c;我们需…...

CSS小技巧之圆形虚线边框

虚线相信大家日常都用的比较多&#xff0c;常见的用法就是使用 border-style 控制不同的样式&#xff0c;比如设置如下边框代码&#xff1a; border-style: dotted dashed solid double;这将设置顶部的边框样式为点状&#xff0c;右边的边框样式为虚线&#xff0c;底部的边框样…...

QString与QByteArray互相转换的方法

QString与QByteArray互相转换的方法 [1] QString与QByteArray互相转换的方法QString转QByteArray方法QByteArray转QString方法QByteArray类同样不以’\0’为结尾QByteArray转QString&#xff0c;主要用buf.toHex()即可 [2] Qt开发串口通讯软件中的数据转换问题1.读取串口命令-Q…...

Springboot +Flowable,设置流程变量的方式(一)

一.简介 为什么需要流程变量。 举个例子&#xff0c;假设有如下一个流程&#xff0c;截图如下&#xff1a; 这是一个请假流程&#xff0c;那么谁请假、请几天、起始时间、请假理由等等&#xff0c;这些都需要说明&#xff0c;不然领导审批的依据是啥&#xff1f;那么如何传递…...

机器学习13(正则化)

文章目录 简介正则化经验风险和结构风险过拟合正则化建模策略 逻辑回归逻辑回归评估器 练习评估器训练与过拟合实验评估器的手动调参 简介 这一节详细探讨关于正则化的相关内容&#xff0c;并就 sklearn 中逻辑回归&#xff08;评估器&#xff09;的参数进行详细解释由于 skle…...

并发编程学习(十一):原子数组、

1、数组类型的原子类 原子数组类型&#xff0c;这个其实和AtomicInteger等类似&#xff0c;只不过在修改时需要指明数组下标。 CAS是按照来根据地址进行比较。数组比较地址&#xff0c;肯定是不行的&#xff0c;只能比较下标元素。而比较下标元素&#xff0c;就和元素的…...

递归到动态规划:省去枚举行为

如果在动态规划的过程中没有枚举行为&#xff0c;那严格位置依赖和傻缓存的方式并没有太大区别&#xff0c;但是当有枚举行为的时候&#xff08;一个位置依赖于多个位置&#xff09;&#xff0c;那严格位置依赖是有优化空间的&#xff0c;枚举行为也许可以省去&#xff0c;题目…...

服务(第二十一篇)mysql高级查询语句(二)

①视图表&#xff1a; 视图表是虚拟表&#xff0c;用来存储SQL语句的定义 如果视图表和原表的字段相同&#xff0c;是可以进行数据修改的&#xff1b; 如果两者的字段不通&#xff0c;不可以修改数据。 语法&#xff1a; 创建&#xff1a;create view 试图表名 as ... 查…...

MYSQL高可用配置(MHA)

1、什么是MHA MHA&#xff08;Master High Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故障切换的过程中最大…...

单精度浮点数与十进制数据相互转换

一、float基础&#xff1a; Float类型占4个字节,也就是32bit,其中最高位是符号位,2~9位是指数位,后边的23bit是数值位.如下所示 大部分数据的二进制形式都可以用科学计数法表示,即1.m*2^n这种形式,只要知道m和n,就能确定一个数值 二、小数位如何转变为二进制&#xff1a; 下面…...

PMP敏捷-4大价值观、12原则

宣言及4大价值观 个体及互动 胜于 流程和工具 以人为本 工作的软件 胜于 完整的文档 以价值为导向 客户合作 胜于 合同谈判 合作共赢 应对变更 胜于 遵循计划 拥抱变化 12原则 工作原则&#xff1a;精益、至简&#xff0c;实现这种原则的方式是“定期反省”。9、10、12 …...

K8S—Helm

一、Helm介绍 helm通过打包的方式&#xff0c;支持发布的版本管理和控制&#xff0c;很大程度上简化了Kubernetes应用的部署和管理。 Helm本质就是让k8s的应用管理&#xff08;Deployment、Service等&#xff09;可配置&#xff0c;能动态生成。通过动态生成K8S资源清单文件&a…...

ALSA内部函数调用流程

ALSA内部函数调用流程 一直都有这样的一个疑问 就是在linux系统中我们调用snd_pcm_open后&#xff0c;就不知道alsa内部是怎么运行的了 用户的pcm_open()相当于先对ASoC各个驱动模块startup()&#xff0c;再做hw_params()。 pcm_open()pcm->fd open("/dev/snd/pcm…...

Python正则表达式详解,保姆式教学,0基础也能掌握正则

正则作为处理字符串的一个实用工具&#xff0c;在Python中经常会用到&#xff0c;比如爬虫爬取数据时常用正则来检索字符串等等。正则表达式已经内嵌在Python中&#xff0c;通过导入re模块就可以使用&#xff0c;作为刚学Python的新手大多数都听说”正则“这个术语。 今天来给…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...