再谈动态SQL
专栏精选
引入Mybatis
Mybatis的快速入门
Mybatis的增删改查扩展功能说明
mapper映射的参数和结果
Mybatis复杂类型的结果映射
Mybatis基于注解的结果映射
Mybatis枚举类型处理和类型处理器
再谈动态SQL
Mybatis配置入门
Mybatis行为配置之Ⅰ—缓存
Mybatis行为配置之Ⅱ—结果相关配置项说明
Mybatis行为配置之Ⅲ—其他行为配置项说明
Mybatis行为配置之Ⅳ—日志
Mybatis整合Spring详解
Mybatis插件入门
Mybatis专栏代码资源
文章目录
- 专栏精选
- 摘要
- 引言
- 正文
- 动态sql标签
- if
- choose...when...otherwise
- where、set
- trim
- foreach
- script
- 动态sql API
- 通过类名和方法名定位SQL
- 通过方法名定位SQL
- 自动定位SQL
- 总结
摘要
在这篇文章中,我们将深入Mybatis动态SQL的世界,了解动态SQL标签和动态sqlAPI的基本方法,其中的很多观点或内容都能在一定程度上让我们的开发之旅更加轻松方便,这是一个菜鸟提升技术能力,老鸟巩固基础知识的好机会。准备好开启今天的神奇之旅了吗?
引言
大家好,我是奇迹老李,一个专注于分享开发经验和基础教程的博主。欢迎来到我的频道,这里汇聚了汇集编程技巧、代码示例和技术教程,欢迎广大朋友们点赞评论提出意见,重要的是点击关注喔 🙆,期待在这里与你共同度过美好的时光🕹️,今天要和大家分享的内容是再谈动态SQL。做好准备,Let’s go🚎🚀
正文

在引入Mybatis一文中我们提到,jdbc对于过长,过复杂,多条件查询的无力感,Mybatis提供动态SQL这一特性解决拼接SQL语句的痛点。在之前的文章中我们已经简单介绍过动态SQL的一些特性,如
- 条件语句if、choose…when…otherwise
- 循环语句foreach
- sql条件语句where、set
除此之外,动态SQL还包含以下几种特性
-
扩展语句trim、bind、script
-
动态sql API
动态sql标签
if
<if test="null != appName and ''.toString() != appName">and app_name like concat('%',#{appName},'%')</if>
<if test="null != authType and ''.toString() != authType">and auth_type = #{authType}</if>
<if test="null != startDate and '' != startDate">and create_date >= #{startDate}</if>
<if test="null != endDate and '' != endDate">and create_date <= #{endDate}</if>
choose…when…otherwise
mybatis映射文件中的 if…else
<choose> <when test="id != null and id > 0">id=#{id}</when> <when test="id <= 0">is_del='0'</when><otherwise>id='1'</otherwise>
</choose>
java代码的同义改写
Intege id=...;
if(id != null && id >0){//id=#{id}
}else if(id <=0){//is_del='0'
}else{//id='1'
}
where、set
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
—— Mybatis官方文档
<!--如果满足if条件,and auth_type = #{authType}中的“and ”将会被删除,并在前边添加“where ”,其结果是-->
<!--where auth_type = #{authType}-->
select * from app_test
<where> <if test="null != authType and ''.toString() != authType">and auth_type = #{authType}</if>
</where>
set 元素用于更新(update)语句中的set部分,和where一样,set的子元素返回的结果结尾如果有“, ”,set元素也会自动将其去除
<!--如果满足if条件,app_name = #{appName},中的“,”会被删除,其他的“,”不会删除,并在语句之前添加“set ”,其结果是-->
<!--update app_test set app_code = #{appCode},app_name = #{appName} where id=#{id}-->
update app_test
<set> <if test="appCode != null and '' != appCode">app_code = #{appCode},</if><if test="appName != null and '' != appName">app_name = #{appName},</if>
</set>
where id=#{id}
trim
trim元素中可以自定义子句应该忽略的内容、和应该添加的内容,where和set标签的功能都能通过trim实现
trim实现where的功能
<!--prefix属性表示将要给子句添加的前缀,prefixOverrides属性表示子句如果出现这样的开头将其忽略-->
select * from app_test
<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="null != authType and ''.toString() != authType">AND auth_type = #{authType}</if>
</trim>
trim实现set的功能
<!--suffixOverrides属性表示子句如果出现这样的结尾将其忽略-->
update app_test
<trim prefix="SET" suffixOverrides=","><if test="appCode != null and '' != appCode">app_code = #{appCode},</if><if test="appName != null and '' != appName">app_name = #{appName},</if>
</trim>
where id=#{id}
trim标签属性详解
| 序号 | 属性名 | 效果说明 |
|---|---|---|
| 1 | prefix | 前缀 |
| 2 | suffix | 后缀 |
| 3 | prefixOverrides | 将被删除的前缀 |
| 4 | suffixOverrides | 将被删除的后缀 |
由此可见,以上的set内容可以写成如下形式
update upp_test set
<trim suffix="where id=#{id}" suffixOverrides=","><if test="appCode != null and '' != appCode">app_code = #{appCode},</if><if test="appName != null and '' != appName">app_name = #{appName},</if>
</trim>
相应的where内容也可以写成如下形式
select * from app_test
<trim prefix="where " prefixOverrides="AND |OR "><if test="null != authType and ''.toString() != authType">AND auth_type = #{authType}</if>
</trim>
foreach
foreach标签用来遍历集合数据
foreach标签的使用方式如下:
insert into app_test(app_name,app_code,auth_type,create_date,creator) values
<foreach collection="list" separator="," item="entity" index="index" open="" close=""> (#{entity.appName},#{entity.appCode},#{entity.authType},#{entity.createDate},#{entity.creator})
</foreach>
foreach标签的属性说明
| 序号 | 属性名 | 属性说明 |
|---|---|---|
| 1 | collection | 集合数据的参数名称 |
| 2 | index | 集合数据的索引方式,一般默认为index |
| 3 | item | 集合内部的元素命名,类似for(T t,List<T>)中的t |
| 4 | open | 左侧需要添加的字符 |
| 5 | close | 右侧需要添加的字符 |
script
Mybatis支持通过注解的形式编写sql语句,主要通过@Select,@Insert,@Update,@Delete几个注解实现,示例如下
@Select("select * from app_test where auth_type=#{type}")
List<AppTestEntity> queryList(@Param("type") String type);
这样就省略了创建xml映射文件的工作,但是这样有一个缺点,就是不方便编写动态sql,这时可以使用script标签
public interface ApplicationRepository {@Update({ "<script>", "update app_test", "<set>", "<if test=\"appStatus != null and appStatus != '' \">app_status=#{appStatus},</if>", "</set>", "where id=#{id}", "</script>" }) int updateByScript(AppTestEntity app);
}
这里需要注意,在字符串中使用 " 双引号字符,需要使用 \ 符号转义,如上例所示
动态sql API
除了通过映射文件使用动态sql的方式之外,Mybatis还提供了基于JavaAPI实现动态sql的方案。(这种方法可以弥补script标签的不足)
通过类名和方法名定位SQL
这里我们针对AppTest对象的条件查询进行改造
public interface ApplicationRepository {//此注解用于标注动态sql生成的类,方法名称@SelectProvider(type = ApplicationSqlProvider.class,method = "queryAppFunc") List<AppTestEntity> queryAppProvider(AppSearchVo param);
}//动态sql生成类
//这个类名称和方法名称需要和@SelectProvider注解标注的类型和方法名称相对应
package top.sunyog.mybatis.provider; import org.apache.ibatis.jdbc.SQL;
import top.sunyog.common.entity.AppSearchVo; public class ApplicationSqlProvider { public static String queryAppFunc(AppSearchVo param){ SQL sql = new SQL() {//静态代码块{ SELECT("*"); FROM("app_test"); if (param.getAppName()!=null && !"".equals(param.getAppName())) { WHERE("app_name like concat('%',#{appName},'%')"); } if (param.getAuthType()!=null && !"".equals(param.getAuthType())){ WHERE("auth_type = #{authType}"); } if (param.getStartDate() != null){ WHERE("create_date >= #{startDate}"); } if (param.getEndDate() != null){ WHERE("create_date <= #{endDate}"); } }}; return sql.toString(); }
}
功能测试类
public class ApplicationService extends MybatisService<ApplicationRepository>{@Override public void doService() { ApplicationRepository mapper = super.getMapper(ApplicationRepository.class); this.testSelectProvider(mapper); }private void testSelectProvider(ApplicationRepository mapper){ AppSearchVo vo = new AppSearchVo(); vo.setAppName("1"); vo.setAuthType("2"); vo.setStartDate(LocalDateTime.of(2023,11,1,12,10)); vo.setEndDate(LocalDateTime.of(2023,11,3,12,10)); List<AppTestEntity> list = mapper.queryAppProvider(vo); list.forEach(System.out::println); }
}
通过方法名定位SQL
除了通过 @SelectProvider注解直接指定类和方法之外,还可以只指定类,但这种方式需要保证Mapper接口的方法名称和 Provider类的方法名称一一对应。
//mapper接口
@SelectProvider(type = ApplicationSqlProvider.class)
List<AppTestEntity> queryAppProvider(AppSearchVo param);//provider类
public class ApplicationSqlProvider implements ProviderMethodResolver { public static String queryAppProvider(AppSearchVo param){...}
}
使用这种方式需要保证两点:
- 方法名称相同
- provider类实现
ProviderMethodResolver接口
自动定位SQL
通过配置默认的 SqlProvider类,可以将所有的 @*Provider 定位到同一个类中,只要保证 mapper接口的方法名称和 Provider类的方法名称相同即可
配置说明
<configuration> <properties resource="..."/><settings> <!--设定默认的 sql provider--><setting name="defaultSqlProviderType" value="top.sunyog.mybatis.provider.ApplicationSqlProvider"/> </settings>...
</configuration>
mapper接口和provider类
//mapper接口
@SelectProvider
List<AppTestEntity> queryAppProvider(AppSearchVo param);//provider类
public class ApplicationSqlProvider implements ProviderMethodResolver { public static String queryAppProvider(AppSearchVo param){...}
}
总结
在MyBatis中,动态SQL是非常有用的特性,它们允许开发者根据不同的条件构建动态的SQL查询,以及更加灵活地生成SQL查询。动态SQL标签提供了灵活的逻辑控制,使我们可以根据不同的条件动态地添加或删除SQL片段。动态SQL API允许我们以编码的方式使用动态SQL,这就相当于在动态SQL之上引入了更加灵活的逻辑处理功能。
你是否曾经使用过MyBatis的动态SQL标签或相关API?如果有,请在评论区分享你的使用经验和心得。如果你还没有使用过这些特性,现在可以用起来了。
📩 联系方式
邮箱:qijilaoli@foxmail.com❗版权声明
本文为原创文章,版权归作者所有。未经许可,禁止转载。更多内容请访问奇迹老李的博客首页
相关文章:
再谈动态SQL
专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…...
【数据结构】树
一.二叉树的基本概念和性质: 1.二叉树的递归定义: 二叉树或为空树,或是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成 2.二叉树的特点: (1)每个结点最多只有两棵子树࿰…...
【Midjourney】AI绘画新手教程(一)登录和创建服务器,生成第一幅画作
一、登录Discord 1、访问Discord官网 使用柯學尚网(亲测非必须,可加快响应速度)访问Discord官方网址:https://discord.com 选择“在您的浏览器中打开Discord” 然后,注册帐号、购买套餐等,在此不做缀述。…...
对比 PyTorch 和 TensorFlow:选择适合你的深度学习框架
目录 引言 深度学习在各行业中的应用 PyTorch 和 TensorFlow 简介 PyTorch:简介与设计理念 发展历史和背景 主要特点和设计理念 TensorFlow:简介与设计理念 发展历史和背景 主要特点和设计理念 PyTorch 和 TensorFlow 的重要性 Pytorch对比Te…...
Oracle笔记-查看表已使用空间最大空间
目前以Oracle18c为例,主要是查这个表USER_SEGMENTS。 在 Oracle 18c 数据库中,USER_SEGMENTS 是一个系统表,用于存储当前用户(当前会话)拥有的所有段的信息。段是 Oracle 中分配存储空间的逻辑单位,用于存…...
大数据HCIE成神之路之特征工程——特征选择
特征选择 1.1 特征选择 - Filter方法1.1.1 实验任务1.1.1.1 实验背景1.1.1.2 实验目标1.1.1.3 实验数据解析1.1.1.4 实验思路 1.1.2 实验操作步骤 1.2 特征选择 - Wrapper方法1.2.1 实验任务1.2.1.1 实验背景1.2.1.2 实验目标1.2.1.3 实验数据解析1.2.1.4 实验思路 1.2.2 实验操…...
python 正则-常见题目
1、邮箱 print(re.findall(r[\w-][\w-]\.[\w-], weidianqq.com))2、身份证号 xxxxxx yyyy MM dd 375 0 十八位 print(re.findall(r(?:18|19|(?:[23]\d))\d{2}, 2010)) # 年print(re.findall(r(?:0[1-9])|10|11|12, 11)) # 月print(re.findall(r(?:[0-2][1-9])|10|20|30|3…...
解析:Eureka的工作原理
Eureka是Netflix开源的一个基于REST的的服务发现注册框架,它遵循了REST协议,提供了一套简单的API来完成服务的注册和发现。Eureka能够帮助分布式系统中的服务提供者自动将自身注册到注册中心,同时也能够让服务消费者从注册中心发现服务提供者…...
RecyclerView 与 ListView 区别和使用
前置知识:ListView基本用法与性能提升 RecyclerView 与 ListView 区别 RecyclerView 需要设置布局(LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager) recyclerView?.layoutManager LinearLayoutManager(activity) …...
力扣232. 用栈实现队列
题目 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty): 实现 MyQueue 类: void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返回队列开…...
这个方法可以让你把图片无损放大
随着数字技术的不断发展,照片无损放大已经成为了摄影领域中的一项重要技术。照片无损放大能够让摄影师在不损失细节和画质的情况下,将照片放大到更大的尺寸,从而让观众能够更加清晰地欣赏到照片中的每一个细节。 今天推荐的这款软件主要是通…...
Springboot整合Elastic-job
一 概述 Elastic-Job 最开始只有一个 elastic-job-core 的项目,定位轻量级、无中心化,最核心的服务就是支持弹性扩容和数据分片!从 2.X 版本以后,主要分为 Elastic-Job-Lite 和 Elastic-Job-Cloud 两个子项目。esjbo官网地址 Ela…...
VsCode的介绍和入门
目录 编辑 介绍 我应该切换到 VS Code 吗?为什么? 入门 Explorer 搜索 源代码控制 调试器 扩展 终点站 命令面板 主题 定制化 不错的配置选项 最适合编码的字体 工作空间 编辑 智能感知 代码格式化 错误和警告 键盘快捷键 键位图…...
C++:自创小游戏
欢迎来玩,每次都有不一样的结果。 长达142行。 #include<bits/stdc.h> #include<windows.h> #define random(a,b) (rand()%(b-a1)a) using namespace std; int main(){int n;cout<<"输1~10,越小越好,不告诉你有什么用,当然也可…...
AIGC带给开发者的冲击
未来会有两种开发者,一种是会使用AIGC工具的开发者另一种是不会使用AIGC的开发者,AIGC的出现提高了开发效率和代码质量,对开发者意味着需要不断学习和适应新的技术和工作范式,开发者可以把更多的精力放在高级抽象的定义以及更高维…...
利用蚁剑钓鱼上线CS
前言 中国蚁剑使用Electron构建客户端软件,Electron实现上用的是Node.js,并且Node.js能执行系统命令,故可以利用蚁剑的webshell页面嵌入js来直接执行命令,进而钓鱼来上线CS。(类似Goby,Goby也是使用Electr…...
宣传照(私密)勿转发
精美的海报通常都是由UI进行精心设计的,现在有100 件商品需要进行宣传推广,如果每个商品都出一张图显然是不合理的,且商品信息各异。因此需要通过代码的形式生成海报。对此,我也对我宣传一波,企图实现我一夜暴富的伟大…...
【Spring】19 AOP介绍及实例详解
文章目录 1. 定义1)什么意思呢?2)如何解决呢? 2. 基本概念1)切面(Aspect)2)切点(Pointcut)3)通知(Advice)4)连…...
ES(Elasticsearch)的基本使用
一、常见的NoSQL解决方案 1、redis Redis是一个基于内存的 key-value 结构数据库。Redis是一款采用key-value数据存储格式的内存级NoSQL数据库,重点关注数据存储格式,是key-value格式,也就是键值对的存储形式。与MySQL数据库不同࿰…...
【JVM面试题】Java中的静态方法为什么不能调用非静态方法
昨晚京东大佬勇哥在群里分享了一道他新创的JVM面试题,我听完后觉得还挺有意思的,分享给大家 小佬们先别急着看我的分析,先自己想想答案 你是不是想说 因为静态方法是属于类的,而非静态方法属于实例对象 哈,有人这样回答…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
