【MyBatis】动态SQL

在这里插入图片描述

文章目录

  • 前言
  • 增加操作
  • \<trim>标签
  • 查询操作
  • \<where>标签
  • 修改操作
  • \<set>标签
  • 删除操作
  • \<foreach>标签
  • \<include>标签

前言

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。具体的定义大家可以参考官方文档MyBatis动态SQL。这篇文章我们将结合动态SQL完成更加复杂的 SQL 操作。

增加操作

想必大家肯定遇到过注册某个账号的时候需要输入自己的相关信息,其中这些信息包括:必填信息和非必填信息,对于这些必填信息,我们只需要在创建表的时候将这个字段设置为非 null 就可以了,而对于那些非必选的选项,我们又该如何定义呢?

这时就需要我们使用动态标签来判断了,对于这些可以传递值和可以不传递值的字段,我们可以使用 <if> 标签来修饰:

@Insert("insert into userinfo(username,`password`,age," +
        "<if test='gender!=null'>gender,</if>" +
        "phone)" +
        "values(#{username},#{password},#{age}," +
        "<if test='gender!=null'>gender,</if>" +
        "#{phone})")
public Integer insertByCondition(UserInfo userInfo);

<if test="">123</if> 这个标签中 test 表示的是判断,当 test 参数中的判断为真时,那么这个标签的结果就为 <if> </if>标签之间的代码块,在这里就是123;如果 test 中的代码块的判断为假的时候,那么这个<if> 标签的结果就是空。

@Test
void insertByCondition() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("彭于晏");
    userInfo.setPassword("123456");
    userInfo.setAge(18);
    userInfo.setPhone("132131231");
    int ret = userInfoMapper.insertByCondition(userInfo);
    log.info(ret + "被更新");
}

然后我们调用这个方法的时候,可以不为 gender 字段传递值,如果不传递值,那么这个字段的值就为创建表时定义的默认值,也可以传递值。然后我们运行一下看能达到效果吗?

在这里插入图片描述
这里为什么会报错呢?因为 <if> 标签是属于 JavaScript 的,所以我们需要使用到 <script> 标签。

@Insert("<script>" +
        "insert into userinfo(username,`password`,age," +
        "<if test='gender!=null'>gender,</if>" +
        "phone)" +
        "values(#{username},#{password},#{age}," +
        "<if test='gender!=null'>gender,</if>" +
        "#{phone})" +
         "</script>")
public Integer insertByCondition(UserInfo userInfo);

在这里插入图片描述
有人会问了,使用 <if> 标签和不使用作用不是一样的吗?对于当前插入数据操作作用是一样的,但是如果我们进行的是修改操作的话,因为我们不知道用户需要修改什么信息,所以我们在写修改操作的话,就需要将所有的字段的修改操作都写上,但是如果我们不使用 <if> 标签的话,并且用户在修改的时候,某个信息没有修改话,后端SQL预处理之后是这样的:update userinfo set username=?, password=?, gender=?, phone=? where username=?,前端传递来的参数是这样的:null, null, null, 123456, 小美,也就是用户只是修改了电话号码这个字段,但是因为没有使用 <if> 标签,所以其他的字段就会被修改为 null,这就会出现问题了。而使用 <if> 标签就会这样处理:update userinfo phone=? where username=?,参数传递:123456, 小美

上面是使用注解的方式来实现 MyBatis 的,实现 MyBatis 不仅可以通过注解来实现,也可以通过 XML 的格式实现。我们看看 XML 如何实现动态 SQL。

首先我们需要告诉 MyBatis 我们的 xml 文件在哪里:

mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml

然后在 XML 文件中写入下面代码,SQL 语句写在 <mapper> 标签中。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatis20240101.mapper.UserInfoMapper">

</mapper>

这里 namespace 的值是我们是使用了 MyBatis 框架操作数据库的类的全限定类名称。

<insert id="insertByCondition">
    insert into userinfo(username,`password`,age,
    <if test="gender!=null">
            gender,
    </if>
    phone)
    values(#{username},#{password},#{age},
    <if test="gender!=null">
        #{gender},
    </if>
     #{phone})
</insert>

因为 XML 文件本身就支持 JavaScript,所以我们这里不需要添加 <script> 标签,不仅如此,使用 XML 文件的方式写 MyBatis 还会有提示,所以书写动态 SQL 建议使用 XML 文件格式。

在这里插入图片描述
但是使用 <if> 标签也会存在问题,假设我们将 phone 字段也设置为动态的,并且在传递值的时候不传递 phone 的话就会出现问题。

<insert id="insertByCondition">
    insert into userinfo(username,`password`,age,
    <if test="gender!=null">
            gender,
    </if>
    <if test="phone!=null">
            phone
    </if>)
    values(#{username},#{password},#{age},
    <if test="gender!=null">
        #{gender},
    </if>
     <if test="phone!=null">
        #{phone}
     </if>)
</insert>
@Test
void insertByCondition() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("彭于晏");
    userInfo.setPassword("123456");
    userInfo.setAge(18);
    //userInfo.setPhone("123456");
    int ret = userInfoMapper.insertByCondition(userInfo);
    log.info(ret + "被更新");
}

在这里插入图片描述
可以看到,因为 phone 是我们定义增加操作时候的最后一个字段,在这里将 phone 设置为动态的,并且在传递值的时候没有传递 phone,那么在拼接 SQL 的时候 insert into userinfo() values() 中两个括号中一定会是以逗号结尾,那么这样的 SQL 就是不和规则的 SQL,那么如何解决呢?这里就需要用到 <trim> 标签了。

<trim>标签

trim 标签在 MyBatis 中主要用于处理 SQL 语句,以去除多余的关键字、逗号,或者添加特定的前缀和后缀。这在进行选择性插入、更新、删除或条件查询等操作时非常有用。

trim 标签有以下属性:

  • prefix:表⽰整个语句块,以prefix的值作为前缀
  • suffix:表⽰整个语句块,以suffix的值作为后缀
  • prefixOverrides:表⽰整个语句块要去除掉的前缀
  • suffixOverrides:表⽰整个语句块要去除掉的后缀

因为我们这里是语句块末尾出现了多余的逗号,所以我们配置 suffixOverrides 属性来删除多余的逗号。

<insert id="insertByCondition">
    insert into userinfo
    <trim prefix="(" suffix=")" suffixOverrides=",">
        username,`password`,age,
        <if test="gender!=null">
                gender,
        </if>
        <if test="phone!=null">
                phone
        </if>
    </trim>
    values
       <trim prefix="(" suffix=")" suffixOverrides=",">
           #{username},#{password},#{age},
           <if test="gender!=null">
               #{gender},
           </if>
           <if test="phone!=null">
               #{phone}
           </if>
       </trim>
</insert>

在这里插入图片描述

查询操作

大家在肯定在网上买过手机吧,当我们买手机的时候,往往会加上一些限制条件。

在这里插入图片描述
而这种加上限制条件的查询就可以看成是 select 后面加了 where 语句,但是由于不知道用户需要加上多少查询时候的限制条件,所以这里就可以使用到动态 SQL。

UserInfo selectByCondition(UserInfo userInfo);
<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo">
    select * from userinfo
    where
        <if test="username!=null">
            username=#{username} 
        </if>
        <if test="password!=null">
             and password=#{password} 
        </if>
        <if test="age!=null">
            and age=#{age} 
        </if>
        <if test="phone!=null">
            and phone=#{phone} 
        </if>
</select>
@Test
void selectByCondition() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("彭于晏");
    userInfo.setPhone("34567");
    log.info(userInfoMapper.selectByCondition(userInfo).toString());
}

在这里插入图片描述
当然这里也会出现问题,就是当第一个 where 子句没有传递值的话,那么 where 子句中就会多一个 and 在开头。

@Test
void selectByCondition() {
    UserInfo userInfo = new UserInfo();
    //userInfo.setUsername("彭于晏");
    userInfo.setPhone("34567");
    log.info(userInfoMapper.selectByCondition(userInfo).toString());
}

在这里插入图片描述
为了解决问题,可以使用 <trim> 标签删除前面多余的 and:

<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo">
    select * from userinfo
    where
        <trim prefixOverrides="and">
            <if test="username!=null">
                username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
            <if test="age!=null">
                and age=#{age}
            </if>
            <if test="phone!=null">
                and phone=#{phone}
            </if>
        </trim>
</select>

在这里插入图片描述

<where>标签

这里是解决了 where 子句中开头出现多余的 and,如果我们一个限制条件都不加入呢?

@Test
void selectByCondition() {
    UserInfo userInfo = new UserInfo();
    //userInfo.setUsername("彭于晏");
    //userInfo.setPhone("34567");
    log.info(userInfoMapper.selectByCondition(userInfo).toString());
}

在这里插入图片描述
这样就会出现问题,那么这样如何解决呢?MyBatis 为我们提供了 <where> 标签用来解决查询语句的 where 子句出现的各种问题,包括开头出现的多余的 and 和 where 子句无内容的情况。

<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo">
    select * from userinfo
    <where>
        <if test="username!=null">
            username=#{username}
        </if>
        <if test="password!=null">
            and password=#{password}
        </if>
        <if test="age!=null">
            and age=#{age}
        </if>
        <if test="phone!=null">
            and phone=#{phone}
        </if>
	</where>
</select>

在这里插入图片描述
可以看到,当我们 where 子句为空的时候,使用 <where> 标签会自动删除 where 子句,它也可以帮助我们删除多余的 and,下面的报错咱们不管,这时因为我们没加 where 子句,所以就相当于查询整个表,但是我们方法的返回值是 UserInfo,改为列表就可以了。

当 where 子句为空的时候,我们还有一种解决方式,就是自己加上一个 1=1 的条件,这样当我们加的条件为空的时候就不会出现错误。

<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo">
    select * from userinfo
    where 1=1
        <trim prefixOverrides="and">
            <if test="username!=null">
                username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
            <if test="age!=null">
                and age=#{age}
            </if>
            <if test="phone!=null">
                and phone=#{phone}
            </if>
        </trim>
</select>

修改操作

这个修改操作就是前面我们举的一个例子,我们并不知道用户会修改哪些信息,所以我们这里就需要使用到动态 SQL 来解决这个问题。

void updateByCondition(UserInfo userInfo);
<update id="updateByCondition">
    update userinfo set
            <if test="password!=null">
                password=#{password},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="gender!=null">
                gender=#{gender},
            </if>
            <if test="phone!=null">
                phone=#{phone}
            </if>
        where
            <if test="username!=null">
                username=#{username}
            </if>
</update>
@Test
void updateByCondition() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("小美");
    userInfo.setPassword("666666");
    userInfo.setPhone("23456");
    userInfoMapper.updateByCondition(userInfo);
}

在这里插入图片描述

<set>标签

为了解决 set 中出现的 set 子句中多余的逗号的问题,可以使用 <set> 标签。

在这里插入图片描述

<update id="updateByCondition">
    update userinfo
    <set>
        <if test="password!=null">
            password=#{password},
        </if>
        <if test="age!=null">
            age=#{age},
        </if>
        <if test="gender!=null">
            gender=#{gender},
        </if>
        <if test="phone!=null">
            phone=#{phone}
        </if>
    </set>    
    <where>
        <if test="username!=null">
            username=#{username}
        </if>
    </where>
</update>

如果 set 子句为空的时候,是会报错的,通过 <set> 标签无法解决,我们应该避免用户 set 输入空的子句。

删除操作

<foreach>标签

当我们想要在删除的时候删除不定数量的条件时,delete from userinfo where name in("小美","小帅"),因为条件的数量是不确定的,所以我们在定义的时候就不知道 where 子句后面有多少个,所以这里我们就需要用到 <foreach> 标签。

<foreach> 标签可以对集合进行遍历,标签具有以下属性:

  • collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
  • item:遍历时的每⼀个对象
  • open:语句块开头的字符串
  • close:语句块结束的字符串
  • separator:每次遍历之间间隔的字符串
void deleteByCondition(List<Integer> list);
<delete id="deleteByCondition">
     delete from userinfo
    where id in
    <foreach collection="list" item="id" open="(" close=")">
        #{id}
    </foreach>
</delete>
@Test
void deleteByCondition() {
    userInfoMapper.deleteByCondition(Arrays.asList(13,18,19,20));
}

在这里插入图片描述

<include>标签

在xml映射⽂件中配置的SQL,有时可能会存在很多重复的⽚段,此时就会存在很多冗余的代码

在这里插入图片描述
我们可以对重复的代码⽚段进⾏抽取,将其通过 <sql> 标签封装到⼀个SQL⽚段,然后再通过
<include> 标签进⾏引⽤。

ArrayList<UserInfo> selectAll();
<sql id="allColumn">
    id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>
<select id="selectAll" resultType="com.example.mybatis20240101.model.UserInfo">
    select
    <include refid="allColumn"></include>
    from userinfo
</select>
@Test
void selectAll() {
    log.info(userInfoMapper.selectAll().toString());
}

在这里插入图片描述
这样就可以减少很多重复的代码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

云原⽣组件Nacos新型红队手法研究

组件简介 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快…

C++代码重用:继承与组合的比较

目录 一、简介 继承 组合 二、继承 三、组合 四、案例说明 4.1一个电子商务系统 4.1.1继承方式 在上述代码中&#xff0c;Order类继承自User类。通过继承&#xff0c;Order类获得了User类的成员函数和成员变量&#xff0c;并且可以添加自己的特性。我们重写了displayI…

java进阶||jdk进阶之循环

从18年学java到现在除了各种各样的数据类型和集合烧不了要遍历这些变量, for循环这时就少不了啦(当然还有8后引入的神器泛型) 先来看一段精髓业务代码, 使用了多个新特性当然也少不了循环和分支判断 代码较长解析在后面 private CommonPage<List<Object>> handle…

NX二次开发PK获取对象类型

PK_ENTITY_ask_class(),获取对象类型建议用这个函数&#xff0c;比较通用&#xff0c;包含所有对象类型&#xff0c;可以替代UF_MODL_ask_edge_type(),UF_MODL_ask_body_type(),UF_MODL_ask_face_type()等函数 PK_ENTITY_t entity; PK_CLASS_t PK_TYPE; PK_ENTITY_ask_class(e…

ubuntu 22 搭建git服务

第一步&#xff0c;安装git&#xff1a; sudo apt-get install git 创建用户信息 git config --global user.name soft 第二步&#xff0c;创建一个git用户&#xff0c;用来运行git服务&#xff1a; sudo adduser git 创建git仓库的存储目录、更改文件目录属主为代码仓库…

观测云产品更新 | 日志、场景仪表板、监控器等

观测云更新 用户访问监测 &#xff08;RUM &#xff09; 公网 Dataway 支持 ip 转换成地理位置信息。 日志 > 查看器详情页 1、新增 BPF 网络日志采集及日志详情页&#xff0c;支持 Json 格式转化&#xff1b; 2、上述 1 中的日志详情页中新增可读的展示模式&#xff0c…

爬虫—响应页面乱码问题解决方法

爬虫—响应页面乱码问题解决方法 案例&#xff1a;腾牛网图片抓取 源代码如下&#xff1a; import requestsurl https://www.qqtn.com/wm/meinvtp_1.html headers {user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) …

牛客周赛 Round 1 解题报告 | 珂学家 | 分类计数 + 同余DP

前言 生于生时&#xff0c;亡于亡刻。遵从自心&#xff0c;尽人之事。 整体评价 终于等来了侧重面试的比赛&#xff0c;而且题量刚刚好&#xff0c;不超纲&#xff0c;不涉及算法竞赛。 第一场的比赛&#xff0c;感觉题目出的比较典&#xff0c;A是简单模拟&#xff0c;B则是…

springcloud sleuth分布式请求链路跟踪

简介 在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果&#xff0c;每一个前段请求都会形成一条复杂的分布式服务调用链路&#xff0c;链路中的任何一环出现高延时或错误都会引起整个请求最后的失败. S…

【深度学习每日小知识】Logistic Loss 逻辑回归

逻辑回归的损失函数 线性回归的损失函数是平方损失。逻辑回归的损失函数是对数损失&#xff0c;定义如下&#xff1a; L o g L o s s ∑ ( x , y ) ∈ D − y log ⁡ ( y ′ ) − ( 1 − y ) log ⁡ ( 1 − y ′ ) LogLoss\sum_{(x,y)\in D}-y\log(y)-(1-y)\log(1-y) LogLoss…

黑马程序员——2022版软件测试——乞丐版——day02

目录&#xff1a; 解决穷举场景 等价类划分法案例&#xff08;qq合法验证&#xff09;案例&#xff08;城市电话验证&#xff09;总结&#xff08;应用场景&#xff09;解决边界限制问题 步骤案例1案例2总结解决多条件有依赖关系测试 介绍步骤案例&#xff08;订单&#xff09…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK实现相机的高速图像保存(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK实现相机的高速图像保存&#xff08;C#&#xff09;&#xff09; Baumer工业相机Baumer工业相机的图像高速保存的技术背景Baumer工业相机通过NEOAPI SDK函数图像高速保存在NEOAPI SDK里实现线程高速图像保存&#xff1a;工业相机高…

LLM主流框架:Causal Decoder、Prefix Decoder和Encoder-Decoder

本文将介绍如下内容&#xff1a; transformer中的mask机制Causal DecoderPrefix DecoderEncoder Decoder总结 一、transformer中的mask机制 在Transformer模型中&#xff0c;mask机制是一种用于在self-attention中的技术&#xff0c;用以控制不同token之间的注意力交互。具体…

Debezium发布历史64

原文地址&#xff1a; https://debezium.io/blog/2019/07/12/streaming-cassandra-at-wepay-part-1/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. 在 WePay 上流式传输 Cassandra - 第 1 部分 七月 12, 2019 …

HackTheBox - Medium - Linux - Faculty

Faculty Faculty 是一台中型 Linux 机器&#xff0c;具有 PHP Web 应用程序&#xff0c;该应用程序使用的库容易受到本地文件包含的影响。利用该库中的 LFi 会泄露一个密码&#xff0c;该密码可用于通过 SSH 以名为“gbyolo”的低级用户身份登录。用户“gbyolo”有权作为“dev…

Leetcode 剑指 Offer II 061. 查找和最小的 K 对数字

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定两个以升序排列的整数数组 nums1 和 nums2 , 以及一个整数 k…

【GNN2】PyG完成图分类任务,新手入门,保姆级教程

上次讲了如何给节点分类&#xff0c;这次我们来看如何用GNN完成图分类任务&#xff0c;也就是Graph-level的任务。 【GNN 1】PyG实现图神经网络&#xff0c;完成节点分类任务&#xff0c;人话、保姆级教程-CSDN博客 图分类就是以图为单位的分类&#xff0c;举个例子&#xff1…

服务器管理平台开发(3)- Web后端

Web服务端 整体架构采用前后端分离形式&#xff0c;后端使用Golang实现&#xff0c;参考Gin框架 1、后端服务 1.1、服务端架构 代码可参考Github开源项目&#xff1a;https://github.com/pbrong/hrms 1.2、服务地址 http://x.x.x.x:8000/api/v1/meta/info http://x.x.x.x:800…

算法第十七天-构造有效字符串的最少插入数

构造有效字符串的最少插入数 题目要求 解题思路 考虑abc的个数 假设答案有n个"abc"组成&#xff0c;那么需要插入的字符个数为 3 ∗ n − l e n ( s ) 3*n - len(s) 3∗n−len(s)。 对于相邻的两个字符x和y&#xff08;x在y左侧&#xff09;&#xff1a; 如果 x…

anaconda创建虚拟环境启动jupyter notebook

1.进入虚拟环境 &#xff08;以环境名为py37_pytorch1.9为例&#xff09; 创建虚拟环境: conda create -n py37_pytorch1.9 python3.7 查看已经创建的虚拟环境&#xff1a; ​​​​​​​conda env list 切换/进入环境&#xff1a; conda activate py37_pytorch1.9 删除环…