数据库操作不再困难,MyBatis动态Sql标签解析

系列文章目录

MyBatis缓存原理
Mybatis的CachingExecutor与二级缓存
Mybatis plugin 的使用及原理
MyBatis四大组件Executor、StatementHandler、ParameterHandler、ResultSetHandler 详解
MyBatis+Springboot 启动到SQL执行全流程



在这里插入图片描述

使用MyBatis,或者MyBatis-plus,有一项重要的开发技能就是写动态sql,动态sql能帮我们省略很多复杂逻辑,也能帮我们解决一些格式问题,所以今天我们就来帮大家掌握这个动态sql

📕作者简介:战斧,从事金融IT行业,有着多年一线开发、架构经验;爱好广泛,乐于分享,致力于创作更多高质量内容
📗本文收录于 MyBatis专栏 ,有需要者,可直接订阅专栏实时获取更新
📘高质量专栏 云原生、RabbitMQ、Spring全家桶 等仍在更新,欢迎指导
📙Zookeeper Redis kafka docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待


一、动态sql是什么?

假设有一个电商网站,需要根据用户提供的不同条件查询商品信息,比如说价格、商品名、类别等等,我们需要写出每个查询信息对应的SQL语句,例如
SELECT * FROM Product WHERE price <= #{price};
SELECT * FROM Product WHERE productname = #{name}
SELECT * FROM Product WHERE type = #{type};

如果这样各种各样的筛选逻辑很多,我们就需要写很多条类似的SQL语句,而且还需要判断入参是否合理,是否为空等等。这样我们的sql就会非常的难以维护。此时,我们就需要利用到MyBatis的动态SQL

MyBatis的动态SQL是指在SQL语句中可以根据不同的条件动态生成不同的SQL语句,以适应不同的需求。动态SQL可以根据条件生成不同的SQL语句,比如WHERE条件、ORDER BY、动态判断等等,从而实现更灵活的SQL编写。动态SQL的主要作用是让SQL语句更加灵活、可重用和易维护,提高应用程序的灵活性和可扩展性

二、动态sql原理

MyBatis动态sql标签的实现原理主要是通过OGNL表达式和Java反射机制来实现的。

首先,MyBatis会将动态sql标签中的OGNL表达式解析成Java代码。解析过程中,MyBatis会根据OGNL表达式的类型来生成相应的Java代码片段。例如,当解析if标签时,如果判断条件中的OGNL表达式为布尔类型,则会生成一个if语句的Java代码片段。

其次,MyBatis会使用Java反射机制获取实体类的属性值,并将属性值传递给动态sql标签中的OGNL表达式进行判断。如果判断条件满足,则会将动态sql标签中的内容添加到SQL语句中;反之,则会忽略动态sql标签中的内容

总体来讲,MyBatis动态sql标签的实现原理就是将OGNL表达式解析成Java代码,并使用Java反射机制获取实体类属性值进行判断,以实现SQL语句的动态生成。myBtais整体运行流程可以看往期的 MyBatis+Springboot 启动到SQL执行全流程

三、动态标签解释

1. if 标签

<if>标签允许你根据条件判断是否包含特定的SQL片段,用于构建灵活的查询语句,该标签的使用方法如下:

  1. 在需要动态生成条件的SQL语句中使用<if>标签。
  2. <if>标签中添加一个test属性,用于指定一个boolean表达式,这个表达式会决定是否将当前条件语句包含在生成的SQL语句中。
  3. <if></if>标签中设置需要动态生成的条件语句。
<select id="getUser" resultMap="userResultMap">SELECT * FROM userwhere 1 = 1<if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if>
</select>

在这个示例中,<if>标签用于根据条件动态生成SQL语句中的条件语句。如果传递到这个方法的参数中包含一个非空的name属性,那么就会在SQL语句中添加一个“name = #{name}”的条件语句;如果包含一个非空的age属性,那么就会在SQL语句中添加一个“age = #{age}”的条件语句。

需要注意的是,使用标签时,必须要用“AND”或“OR”将多个<if>标签组合在一起,否则会出现语法错误。此外,还需要注意在使用标签时,SQL语句的正确性和性能的折中。太多的动态条件可能会导致SQL语句生成的复杂度增加,从而影响查询的性能

2 choose、when 和 otherwise 标签

<choose><when><otherwise>标签通常一起使用来实现条件分支查询。这种方式类似于Java中的switch语句或者if-else语句

  • 其中,<choose>标签类似于Java中的switch语句,表示一个选择分支,包含多个<when><otherwise>标签。
  • <when>标签类似于Java中的case语句,用于指定分支条件。当条件满足时,执行<when>标签内的SQL语句。
  • <otherwise>标签类似于Java中的default语句,用于指定默认条件分支。当其他分支条件都不满足时,执行<otherwise>标签内的SQL语句。
<select id="getUsersByAgeAndName" parameterType="map" resultMap="userMap">SELECT * FROM users<where><choose><when test="age != null and name != null">age = #{age} AND name = #{name}</when><when test="age != null">age = #{age}</when><when test="name != null">name = #{name}</when><otherwise>1=1</otherwise></choose></where>
</select>

如上,我们使用<choose><when><otherwise>标签来实现条件分支查询。这个查询可以根据参数中的年龄和名字来查找用户记录,也可以只根据年龄或者名字来查找,或者不加任何条件查询所有的用户记录

3 trim 标签

<trim>标签用于修剪生成的SQL语句,可以用于移除不必要的关键字,特别是在拼接多个条件时非常有用,其主要是用于确定子句中的前缀与后缀。

<trim> 标签具有以下属性:
prefixOverrides:要删除的前缀字符串,可以以“|”分割,添加多个词
suffixOverrides:要删除的后缀字符串,可以以“|”分割,添加多个词
prefix:要添加到 SQL 语句的前缀字符串
suffix:要添加到 SQL 语句的后缀字符串

<select id="findUserByName" parameterType="java.lang.String" resultType="User">SELECT * FROM user<trim prefix="WHERE" prefixOverrides="OR | AND "><if test="name != null and name != ''">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></trim>
</select>

在这个示例中, <trim> 标签用于动态构建 WHERE 子句。如果它会删除前缀中的 OR 或 AND 关键字,并将 WHERE 关键字添加到 SQL 语句中。

当然,你也许会想,如果我把 prefixOverrides 和 prefix 是设置成一个词,那这个词还会不会插入到子句句首呢?答案是会的,因为这里的逻辑是先删除prefixOverrides指定字符串,再添加prefix指定字符串

4 foreach 标签

标签用于循环处理集合类型的参数,生成多次重复的SQL片段,该标签可以用于循环遍历一个集合或数组,并在SQL语句中使用它的值。以下是标签的使用方法:。

遍历集合

<select id="findUserById" parameterType="java.lang.Integer">SELECT * FROM table WHERE id IN<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach>
</select>

其中,collection属性指定要遍历的集合,item属性指定集合中每个元素的别名,open属性指定在开始时添加的字符串,separator属性指定在每个元素之间添加的字符串,close属性指定在结束时添加的字符串。

例如,如果list是一个包含1、2、3的List对象,这个标签会生成以下SQL语句:

SELECT * FROM table WHERE id IN (1,2,3)

遍历数组

<foreach collection="array" item="item" open="(" separator="," close=")">#{item}
</foreach>

这个标签与遍历集合的标签非常相似,只不过把collection属性改为了数组对象

遍历Map

假设我们有这样一个map

Map<String, Object> searchCriteria = new HashMap<>();
searchCriteria.put("username", "john");
searchCriteria.put("age", 25);
searchCriteria.put("city", "New York");

然后我们配上这样的xml

<select id="searchUsers" parameterType="map" resultType="User">SELECT * FROM user<foreach collection="searchCriteria" item="value" index="key" separator="AND" open="WHERE">${key} = #{value}</foreach>
</select>

其中,collection属性指定要遍历的Map集合,index属性指定键的别名,item属性指定值的别名,open属性指定在开始时添加的字符串,separator属性指定在每个元素之间添加的字符串。

最后我们能得到这样的sql

SELECT * FROM user WHERE username = "john" AND age = 25 AND city = "New York"

5 set 标签

标签通常用于动态生成UPDATE语句的SET部分,可自动生成逗号分隔的键值对,<set>标签通常包含多个标签或者标签,用于动态生成要更新的列和值。

  UPDATE user<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="email != null">email = #{email},</if></set>WHERE id = #{id}
</update>

在这个示例中,标签根据传入的参数动态生成SET部分。set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号

6 bind 标签

<bind>用于将一个表达式绑定到一个变量上。绑定的变量可以在后面的SQL语句中引用。通常,标签被用于简化SQL语句中的重复表达式,提高SQL语句的可读性和可维护性。

<select id="findUsers" parameterType="String" resultMap="userResultMap"><bind name="pattern" value="'%' + name + '%'"/>SELECT * FROM user WHERE name LIKE #{pattern}
</select>

在上面的例子中,标签用于将一个字符串表达式绑定到名为pattern的变量上。变量的值是一个由‘%’,‘name’和‘%’组成的字符串,其中‘name’是一个参数。在后面的SQL语句中,#{pattern}即可引用绑定的变量。

在标签中,name属性指定变量的名称,value属性指定变量的值。可以在value属性中使用表达式,变量名和参数Map中的键。

除了绑定表达式到变量上,标签还可以用于绑定参数值到变量上。例如:

<select id="findUsersByAge" parameterType="int" resultMap="userResultMap"><bind name="minAge" value="age - 5"/><bind name="maxAge" value="age + 5"/>SELECT * FROM user WHERE age BETWEEN #{minAge} AND #{maxAge}
</select>

在上面的例子中,<bind>标签用于将一个整数参数值绑定到名为minAge和maxAge的变量上。变量的值是通过计算参数值得到的。在后面的SQL语句中,#{minAge}和#{maxAge}即可引用绑定的变量。

总之,<bind>标签是一个非常强大的MyBatis功能,可以提高SQL语句的可读性和可维护性。在开发MyBatis应用程序时,应该灵活使用标签,以便更好地管理SQL语句。

7 sql 与 include 标签

<sql>标签用于定义可重用的SQL片段。在一个或多个SQL语句中,可以使用标签来引用这些SQL片段。


<sql id="userColumns">id, username, age
</sql><select id="getAllUsers" resultType="User">SELECT <include refid="userColumns" />FROM user
</select><select id="getUsersByName" parameterType="String" resultType="User">SELECT <include refid="userColumns" />FROM userWHERENAME = #{name}
</select>

上述例子中,<include>标签引用了一个名为"userColumns"的SQL片段。引用的方式是通过refid属性指定片段的ID。所有引用的SQL片段都会被添加到原始SQL语句中。这样,其他sql,如果也是使用这几个字段,则都可以引用

8. where 标签

<where> 标签的作用是用来包裹一个或多个 SQL 条件语句,如果这些条件都满足,则会被加入到 SELECT 或 UPDATE 语句中,否则不会。它的使用方法如下:

1.将多个条件语句用 标签包裹起来。

<select id="selectByCondition" parameterType="java.util.Map" resultMap="userMap">select * from user<where><if test="name != null and name!=''">and name like CONCAT('%',#{name},'%')</if><if test="age != null">and age = #{age}</if></where>
</select>

2.在 SQL 语句中使用 <where> 标签时,会自动移除掉多余的 AND 或 OR,而在没有任何条件语句时,<where> 标签也会自动删除自身,以避免 SQL 语法错误。例如,上述代码在没有任何条件时,生成的 SQL 语句为:

select * from user

而当输入 age=20 时,生成的 SQL 语句为:

select * from userwhere age = 20

可以看出<where> 标签是 MyBatis 中一个非常有用的标签,它可以帮助我们更加方便地动态构建 SQL 语句,提高代码的可读性和可维护性。


总结

MyBatis的动态SQL功能极大地简化了数据库操作,使得针对不同查询需求的SQL语句生成变得灵活而高效。通过深入了解和掌握<if>、<choose>、<when>、<otherwise>、<trim>、<foreach>、<set>、<bind>和<sql>等核心标签,开发人员将大幅度减少因为参数不同,而写不同sql的麻烦,此时的sql会自己按预设逻辑变成不同的样式。因此,需要牢牢掌握。

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

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

相关文章

Centos7.6 安装mysql过程全记录

在centos 7.6上 离线安装mysql 的步骤&#xff0c;可参考下文&#xff1a; 一、查看当前MySQL的安装情况并卸载 1. 查看当前MySQL的安装情况 查找之前是否安装了MySQL rpm -qa|grep -i mysql 2.卸载mysql 如果已经安装mysql&#xff0c;则需要先停止MySQL&#xff0c;再删除…

YOLOv5、YOLOv8改进:MobileViT:轻量通用且适合移动端的视觉Transformer

MobileViT: Light-weight, General-purpose, and Mobile-friendly Vision Transformer 论文&#xff1a;https://arxiv.org/abs/2110.02178 1简介 MobileviT是一个用于移动设备的轻量级通用可视化Transformer&#xff0c;据作者介绍&#xff0c;这是第一次基于轻量级CNN网络性…

LeetCode150道面试经典题--单词规律(简单)

1.题目 给定一种规律 pattern 和一个字符串 s &#xff0c;判断 s 是否遵循相同的规律。 这里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。 2.示例 pattern"abba" s "c…

SpingBoot-Vue前后端——实现CRUD

目录​​​​​​​ 一、实例需求 ⚽ 二、代码实现 &#x1f3cc; 数据库 &#x1f440; 后端实现 &#x1f4eb; 前端实现 &#x1f331; 三、源码下载 &#x1f44b; 一、实例需求 ⚽ 实现一个简单的CRUD&#xff0c;包含前后端交互。 二、代码实现 &#x1f3cc; 数…

约束综合中的逻辑互斥时钟(Logically Exclusive Clocks)

注&#xff1a;本文翻译自Constraining Logically Exclusive Clocks in Synthesis 逻辑互斥时钟的定义 逻辑互斥时钟是指设计中活跃&#xff08;activate&#xff09;但不彼此影响的时钟。常见的情况是&#xff0c;两个时钟作为一个多路选择器的输入&#xff0c;并根据sel信号…

八、解析应用程序——分析应用程序(1)

文章目录 一、确定用户输入入口点1.1 URL文件路径1.2 请求参数1.3 HTTP消息头1.4 带外通道 二、确定服务端技术2.1 提取版本信息2.2 HTTP指纹识别2.3 文件拓展名2.4 目录名称2.5 会话令牌2.6 第三方代码组件 小结 枚举尽可能多的应用程序内容只是解析过程的一个方面。分析应用程…

小龟带你敲排序之冒泡排序

冒泡排序 一. 定义二.题目三. 思路分析&#xff08;图文结合&#xff09;四. 代码演示 一. 定义 冒泡排序&#xff08;Bubble Sort&#xff0c;台湾译为&#xff1a;泡沫排序或气泡排序&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元…

【深度学习】再谈向量化

前言 向量化是一种思想&#xff0c;不仅体现在可以将任意实体用向量来表示&#xff0c;更为突出的表现了人工智能的发展脉络。向量的演进过程其实都是人工智能向前发展的时代缩影。 1.为什么人工智能需要向量化 电脑如何理解一门语言&#xff1f;电脑的底层是二进制也就是0和1&…

Arduino+esp32学习笔记

学习目标&#xff1a; 使用Arduino配置好蓝牙或者wifi模块 学习使用python配置好蓝牙或者wifi模块 学习内容&#xff08;笔记&#xff09;&#xff1a; 一、 Arduino语法基础 Arduino语法是基于C的语法,C又是c基础上增加了面向对象思想等进阶语言。那就只记录没见过的。 单多…

全国各城市-货物进出口总额和利用外资-外商直接投资额实际使用额(1999-2020年)

最新数据显示&#xff0c;全国各城市外商直接投资额实际使用额在过去一年中呈现了稳步增长的趋势。这一数据为研究者提供了对中国外商投资活动的全面了解&#xff0c;并对未来投资趋势和政策制定提供了重要参考。 首先&#xff0c;这一数据反映了中国各城市作为外商投资的热门目…

Effective Java笔记(31)利用有限制通配符来提升 API 的灵活性

参数化类型是不变的&#xff08; invariant &#xff09; 。 换句话说&#xff0c;对于任何两个截然不同的类型 Typel 和 Type2 而言&#xff0c; List<Type1 &#xff1e;既不是 List<Type 2 &#xff1e; 的子类型&#xff0c;也不是它的超类型 。虽然 L ist<String…

Linux 文件查看命令

一、cat命令 1.cat文件名&#xff0c;查看文件内容&#xff1a; 例如&#xff0c;查看main.c文件的内容&#xff1a; 2.cat < 文件名&#xff0c;往文件中写入数据&#xff0c; Ctrld是结束输入 例如&#xff0c;向文件a.txt中写入数据&#xff1a; 查看刚刚写入a.txt的…

Yolov5(一)VOC划分数据集、VOC转YOLO数据集

代码使用方法注意修改一下路径、验证集比例、类别名称&#xff0c;其他均不需要改动&#xff0c;自动划分训练集、验证集、建好全部文件夹、一键自动生成Yolo格式数据集在当前目录下&#xff0c;大家可以直接修改相应的配置文件进行训练。 目录 使用方法&#xff1a; 全部代码…

解决监督学习,深度学习报错:AttributeError: ‘xxx‘ object has no attribute ‘module‘!!!!

哈喽小伙伴们大家好呀&#xff0c;很长时间没有更新啦&#xff0c;最近在研究一个问题&#xff0c;就是AttributeError: xxx object has no attribute module 今天终于是解决了&#xff0c;所以来记录分享一下&#xff1a; 我这里出现的问题是&#xff1a; 因为我的数据比较大…

SQL优化

一、插入数据 优化 1.1 普通插入&#xff08;小数据量&#xff09; 普通插入&#xff08;小数据量&#xff09;&#xff1a; 采用批量插入&#xff08;一次插入的数据不建议超过1000条&#xff09;手动提交事务主键顺序插入 1.2 大批量数据插入 大批量插入&#xff1a;&…

数据结构:力扣OJ题

目录 ​编辑题一&#xff1a;链表分割 思路一&#xff1a; 题二&#xff1a;相交链表 思路一&#xff1a; 题三&#xff1a;环形链表 思路一&#xff1a; 题四&#xff1a;链表的回文结构 思路一&#xff1a; 链表反转&#xff1a; 查找中间节点&#xff1a; 本人实力…

YOLOv8+ByteTrack多目标跟踪(行人车辆计数与越界识别)

课程链接&#xff1a;https://edu.csdn.net/course/detail/38901 ByteTrack是发表于2022年的ECCV国际会议的先进的多目标跟踪算法。YOLOv8代码中已集成了ByteTrack。本课程使用YOLOv8和ByteTrack对视频中的行人、车辆做多目标跟踪计数与越界识别&#xff0c;开展YOLOv8目标检测…

第一百二十七天学习记录:我的创作纪念日

机缘 今天收到CSDN官方的来信&#xff0c;想想也可以对我前面的学习记录进行一个总结。 关于来到CSDN的初心&#xff0c;也就是为了让自己养成一个良好的学习总结的习惯。这里要感谢我C语言视频教程的老师&#xff0c;是他建议学生们在技术博客中进行记录。对于技术博客&…

web-Element

在vueapp里<div><!-- <h1>{{message}}</h1> --><element-view></element-view></div> <div><!-- <h1>{{message}}</h1> --><element-view></element-view></div>在view新建个文件 <t…

vue或uniapp使用pdf.js预览

一、先下载稳定版的pdf.js&#xff0c;可以去官网下载 官网下载地址 或 pdf.js包下载(已配置好&#xff0c;无需修改) 二、下载好的pdf.js文件放在public下静态文件里&#xff0c; uniapp是放在 static下静态文件里 三、使用方式 1. vue项目 注意路径 :src"static/pd…