首页 > 数据库 >【SpringBootWeb入门-17】Mybatis-基础操作-动态SQL

【SpringBootWeb入门-17】Mybatis-基础操作-动态SQL

时间:2023-12-21 23:22:06浏览次数:38  
标签:17 标签 SQL SpringBootWeb gender import 动态 where

1、章节回顾

上一篇文章我们讲解完了Mybatis基础操作,本篇继续学习Mybatis中非常重要的功能:动态SQL

什么是动态SQL:随着用户的输入或外部条件的变化而变化的SQL语句,我们称为动态SQL。简单说SQL语句不是固定的,是动态变化的。

就拿我们上一篇所提到的根据条件来查询员工的SQL语句来说:根据条件姓名、性别、入职时间来查询员工表emp数据,我们的案例是三个条件都指定了,SQL语句已经固定的了,如果三个条件都为空,这样子是查不到数据的;或者说只想根据姓名来查询,后面两个条件为空,这样子也无法查到数据。

实际上,我们的SQL语句应该灵活可变的,如果想只根据姓名查询,那么后面的性别、入职时间条件就不需要;如果想根据查询性别,那么姓名、入职时间条件也不需要。简单来说,就是根据实际情况来灵活变化查询SQL(动态SQL)。

那么动态SQL是怎么实现的呢?Mybatis给我们提供了基于这种场景的动态SQL标签:if标签。if标签是用来做条件判断的,如果条件成立,才会去拼接SQL语句,如果不成立就不拼接。

下面我们来通过动态SQL标签来改造上一篇的根据条件来查询员工的SQL语句。

2、动态SQL-if标签

上一篇的根据条件来查询员工的SQL语句如下:

<select id="list" resultType="com.hiker.pojo.Emp">
        select *
        from emp
        where name like concat('%', #{name}, '%')
          and gender = #{gender}
          and entrydate between #{begin} and #{end}
        order by update_time desc
    </select>
View Code

我们通过if标签来改造成动态SQL,改造如下:

<select id="list" resultType="com.hiker.pojo.Emp">
        select *
        from emp
        where
        <if test="name != null">
            name like concat('%', #{name}, '%')
        </if>

        <if test="gender != null">
            and gender = #{gender}
        </if>

        <if test="begin != null and end != null">
            and entrydate between #{begin} and #{end}
        </if>

        order by update_time desc
    </select>
View Code

我们把测试程序的代码参数修改一下,入职时间设置为空:List<Emp> empList = empMapper.list("张", (short) 1, null,null)

重新运行测试代码,输出的结果可以看到,SQL是动态生成:

2、动态SQL-where标签

上面使用if标签改造的动态SQL语句已经成功执行输出,但是这里还有个问题,就是如果第一个字段name我们不查询,只根据查询性别来查询,那么就会报错如下:

此时生成的动态SQL就会变成:select * from emp where and gender = ?  order by update_time desc

很明显出现了SQL错误,where关键字后面紧跟着了and关键字。这是因为我们上面的动态SQL使用的if标签,where后面的第一个条件为空跳过了,接着是第二个条件“and gender = ?”,所以拼接出来的SQL语句出现语法错误。

那么如何来解决这个问题呢?我们可以通过使用where标签来解决这个问题。

<where>标签:where元素只会在子元素有内容的情况下才插入where字句,而且会自动去除字句的开头的AND或者OR关键字。

例如上面的动态SQL语句,使用了where标签后,那么where后面的第一个条件为空跳过了,接着是第二个条件“gender = ?”,会自动去除掉and关键字,生成的动态SQL就不会再有语法报错。

下面我们来重新改造动态SQL语句:

<select id="list" resultType="com.hiker.pojo.Emp">
        select *
        from emp
        <where>
            <if test="name != null">
                name like concat('%', #{name}, '%')
            </if>

            <if test="gender != null">
                and gender = #{gender}
            </if>

            <if test="begin != null and end != null">
                and entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc
    </select>
View Code

改造后我们重新运行测试代码,输出SQL语句:select * from emp WHERE gender = ? order by update_time desc,结果如下:

同理,如果遇上update的SQL语句,也会有set标签,动态地在行首插入 SET 关键字,并会删掉额外的逗号。

3、动态SQL-foreach标签

接下来我们来继续学习动态SQL的foreach标签,这个标签用来做循环遍历,foreach标签里面有几个属性,分别如下:

  • collection:要遍历的集合
  • item:遍历出来的元素
  • separator:每一次遍历使用的分隔符
  • open:遍历开始前要拼接的SQL片段
  • close:遍历结束后要拼接的SQL片段

我们以一个批量删除员工表数据(批量删除id为14、15、16的数据)为例,来看看如何使用foreach标签。

批量删除的SQL代码:delete from emp where id in (14,15,16)

对应的接口方法:

package com.hiker.mapper;

import com.hiker.pojo.Emp;
import org.apache.ibatis.annotations.*;

import java.time.LocalDate;
import java.util.List;

@Mapper
public interface EmpMapper {

    //批量删除员工
    public void deleteByIds(List<Integer> ids);
}
View Code

对应的XML映射文件:

<delete id="deleteByIds">
        delete from emp where id in
        <foreach collection="Ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
View Code

对应的测试代码:

package com.hiker;

import com.hiker.mapper.EmpMapper;
import com.hiker.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {

    @Autowired
    private EmpMapper empMapper;

    //批量删除员工
    @Test
    public void testDeleteByIds() {
        List<Integer> ids = Arrays.asList(14,15,16);
        empMapper.deleteByIds(ids);
    }

}
View Code

运行单元测试的方法,看到动态SQL语句已正确生成并且成功执行批量删除三条记录。

这里有个新增接口小技巧:在EmpMapper里面新增deleteByIds接口,在写完后可以光标放在接口名上,按住Alt+回车+回车,会自动生成XML里面的SQL代码标签,如下图:

4、动态SQL-sql&include标签

接下来我们来继续学习动态SQL的sql、include两个标签,这两个标签是配套使用的。在学习这两个标签之前,我们先来分析一下上面写的SQL代码所存在的问题。

我们可以看到,上面截图的sql代码有两个查询模块,一个是根据多个条件来查询数据,一个是根据id来查询数据,但是我们可以看到,两段查询模块都有相同的部分:

select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp

如果在一个XML映射文件中存在大量的相同性sql代码,如果在项目后期需要修改这块相同的sql代码,那么需要修改的地方就会很多,容易出错,代码复用性较差,这个时候我们就可以使用sql、include两个标签来解决这个问题。

  • <sql>:定义可重用的 SQL 片段。
  • <include>:通过属性refid,指定包含的sql片段。

对应的XML映射文件代码改造:

    <!--  抽取共用的SQL语句 -->
    <sql id="commonSelect" >
        select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
        from emp
    </sql>

    <select id="list" resultType="com.hiker.pojo.Emp">
        <!--  引用进来共用的SQL语句 -->
        <include refid="commonSelect"></include>
        <where>
            <if test="name != null">
                name like concat('%', #{name}, '%')
            </if>

            <if test="gender != null">
                and gender = #{gender}
            </if>

            <if test="begin != null and end != null">
                and entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc
    </select>
View Code

重新运行查询的测试方法,可以看到查询记录也可以正常加载。

关于Mybatis的相关知识点我们已经全部学习完毕了,下一篇我们就开始结合前端来做SpringBootWeb案例小项目学习。

标签:17,标签,SQL,SpringBootWeb,gender,import,动态,where
From: https://www.cnblogs.com/hiker0412/p/17920139.html

相关文章

  • Mac安装Mysql5.7
    官网https://downloads.mysql.com/archives/community/下载Mysql5.7.31,再高5.7版本没有macOS选项安装安装完毕会弹出帐号密码这里要把密码记住,待会用mysqladmin重置密码时要用。启动mysql服务系统设置拉到最下面,启动mysql服务启动成功设置mysql服务端mysql命令别名......
  • 读程序员的README笔记17_构建可演进的架构(下)
    1. 可演进的API1.1. 随着需求的变化,你需要改变你的API,即代码之间的共享接口1.2. 改变API很容易,但很难做到正确1.3. 保持API小巧1.3.1. 小巧的API更易于理解和演进1.3.2. 只添加即刻需要的API方法或字段1.3.3. 带有许多字段的API方法应该有合理的默认值1.3.3.1. 开......
  • MySQL8.0 OCP 70题
    Choosetwo.WhichtwoMySQLServeraccountsarelockedbydefault?默认情况哪两个MySQLServer帐户被锁定?A)anynewROLEaccounts任何新的角色帐户B)anyinternalsystemaccounts任何内部系统帐户C)anyusercreatedwithausername,butmissingthehostnameD......
  • [CF17E] Palisection 题解
    [CF17E]Palisection题解思路直接统计相交的字符串很难数,考虑正难则反。用总共的回文串对数减去相离的回文串个数。设总共有\(tot\)个回文串,用manacher跑出来每个位置的最大回文半径后,使用差分的技巧保存两个数组:\(f_i\)表示以\(i\)为开头的回文串个数,\(g_i\)表示以......
  • 如何判断一个sql走不走索引?
    在你的工作中,可能写过很多个sql,我相信最让你头疼的,一定还是那风骚的查询sql。我猜你很可能有这样的体验,好不容易写了一个牛逼哄哄的查询sql,兴奋的上线投产。结果,在第二天阳光明媚的日子里,dba把你的sql揪了出来,揪出来还不算,dba还要发到群里@你,说:“嗨,兄弟,瞅瞅你写的个啥?”接着,dba......
  • [Codeforces] CF1817A Almost Increasing Subsequence
    CF1817AAlmostIncreasingSubsequence题意给定长度为\(n\)一个序列\(a\)以及\(q\)次询问,每次询问给出\(l\)和\(r\),找出序列\(a\)在\([l,r]\)内最长的几乎递增子序列。对于几乎递增的定义:如果一个序列中不存在连续的三个数\(x\),\(y\),\(z\),使得\(x\gey\ge\......
  • Safari 17信任站点修改造成的工商银行网银控件无法正常使用
    MacOS14.1中,Safari浏览器版本17.1,变更了信任站点流程。在工商银行使用JSP技术开发的网页上存在点击“在此网站上启用”但是检测不到扩展已安装的问题。原因工行个人网银登录网⻚使用jsp开发,⻚面情况非常复杂,嵌套了多个不同网址。通过日志可以发现还请求了epass.icbc.com.......
  • 同样的SQL,怎么突然就慢了?
    本篇文章素材来源于某银行系统的一次性能问题分析。许久没写这种troubleshooting类型的技术文章了,因为曾在服务公司呆过多年,工作原因,这方面之前做的多,听的更多,导致已经达到在自己认知维度下的一个小瓶颈,纯技术型的问题,稍微常见的基本都遇到过,非常少见的也基本是bug类(软件缺陷只能......
  • 在 MySQL 中,你可以使用 `AVG()` 函数来计算一组值或表达式的平均值。`AVG()` 函数的基
    在MySQL中,AVG()函数在计算平均值时会自动忽略NULL值¹⁴。也就是说,它只会计算所有非空值的平均值³。例如,假设你有一个包含以下值的列:90,80,70,85,95,NULL,NULL。在这种情况下,AVG()函数将只计算非空值的平均值,即:而不是将NULL值视为0并计算所有值的平均值。如果你需......
  • PostgreSQL从入门到精通教程 - 第39讲:数据库完全恢复
       PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUGPG技术大讲堂。 第39讲:数据库完全恢复 PostgreSQL第39讲:12月23日(......