【MyBatis】动态SQL

发布于:2025-02-25 ⋅ 阅读:(11) ⋅ 点赞:(0)

 

目录

 前言

预备工作

插入操作

if

trim

查询操作

 where

修改操作

set

删除操作

foreach

include


前言

在前一篇中,我们已经讲解了Mybatis的基本使用,那么本篇我们就进一步来了解Mybatis的功能。

本篇我们主要讲的是Mybatis中的动态SQL,在前面我们的学习中,我们编写的SQL语句基本都是固定的,每个参数都需要传递,而通过动态SQL,它能根据我们所给的参数,动态地选择参数,不需要每个都传递。

想了解更多关于动态SQL的可以看下面的网址:

MyBatis 动态SQL与数据准备http://www.mybatis.cn/mybatis/1942.html

预备工作

首先我们需要配置好项目,在pom.xml配置文件中导入mybatis依赖。

        <dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.4</version>
			<scope>test</scope>
		</dependency>

由于后序基本是用XML的形式来讲解,所以这里我们需要在application.yml文件中添加相应的配置

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 
    map-underscore-to-camel-case: true 
  mapper-locations: classpath:mapper/**Mapper.xml

后序以userinfo为操作对象,这里我们需要添加对应的类。

package com.example.demo.Model;
import lombok.Data;
import java.util.Date;

@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

 创建对应的数据表:

DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info(
      `id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
      `user_name` VARCHAR(256) NOT NULL,
      `password` VARCHAR(256) NOT NULL,
      `age` INT NOT NULL,
      `gender` INT NOT NULL,
      `phone` VARCHAR(256) NOT NULL,
      `delete_flag` INT DEFAULT 1 COMMENT '0-删除 1-存在',
      `create_time` DATETIME DEFAULT NOW(),
      `update_time` DATETIME DEFAULT NOW() ON UPDATE NOW()
)ENGINE = INNODB DEFAULT CHARSET=utf8mb4;

在我们的mapper.xml文件中编写SQL语句

 其中的namespace是我们使用了MyBatis框架操作数据库的类的全限定类名称

<?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.demo.mapper.UserInfoXMLMapper">

插入操作

我们有时候在注册账号的时候,或许会遇到要求我们输入自己的相关信息,其中这些信息包括:个人姓名、身份证号码等等,当然也存在着一些非必填的数据,那么对于这样的情况,我们在后端插入语句应该怎么写呢?

if

这就需要用到我们的<if>标签了,<if>标签能够帮助我们判断哪些字段是不为空的,再拼接到SQL语句中

 public Integer insert(UserInfo userInfo);

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

进行单元测试:

 @Test
    void insert() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("666");
        userInfo.setPassword("123456");
        userInfo.setAge(18);
        userInfo.setGender(1);
        userInfoXMLMapper.insert(userInfo);
        //{} 表示占位符 , 后面的参数会依次替换占位符
        log.info("获取到的id为:{}", userInfo.getId());
    }

当运行起来后,代码报错,是因为在上面中,我们给gender设置了为动态,且在传参的时候目传递,这就会导致在拼接SQL语句的时候,两个括号的结尾一定是逗号,这样的SQL语句是执行了不了的。

那么有没有什么办法去掉这两个逗号呢?

这里就需要用到我们的 <trim>标签了。

trim

<trim>标签能够帮助我们处理在SQL语句中多余的关键字、逗号等,也可以添加特定的前缀以及后缀。

<trim>标签有以下属性:

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

那么我们就可以对上面的SQL语句进行修改:

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

可以看到,这次能够成功的将数据插入。

查询操作

我们在购物等场景的情况下,系统会根据我们的筛选条件,动态地组装where条件。

 这种其实可以看过在select语句后加上where来筛选,但由于不知道用户要选多少个条件,所以这里我们就可以用动态SQL。

    public List<UserInfo> selectByCondition(UserInfo userInfo);
 <select id="selectByCondition" resultType="com.example.demo.Model.UserInfo">
        select * from user_info where
        <trim prefixOverrides="and">
            <if test="userName!=null">
                username = #{userName}
            </if>
            <if test="password!=null">
                and password = #{password}
            </if>
            <if test="phone!=null">
                and phone = #{phone}
            </if>
            <if test="age!=null">
                and age = #{age}
            </if>
        </trim>
    </select>
 @Test
    void selectByCondition() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("小红");
        userInfo.setAge(18);
        userInfo.setGender(1);
        userInfoXMLMapper.selectByCondition(userInfo).forEach(userInfo1 -> log.info(userInfo1.toString()));
    }

当然,这样其实还会有问题,如果我们没有选条件,那么是怎么样的?

 

我们可以看到,这里多出来了一个where,那么应该怎么去除呢?

这里就需要用到我们的<where>标签。

 where

<where>能够帮助我们自动将where子句删除,此外还能够帮助我们删除多余的and

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

修改操作

我们在修改信息的时候,有时也只修改一些信息,而不用全部修改,那么这样的语句应该如何实现?

也是需要用到动态SQL。

    public Integer updateByCondition(UserInfo userInfo);
   <update id="updateByCondition">
        update user_info set
        <trim suffixOverrides=",">
            <if test="userName!=null">
                username = #{userName},
            </if>
            <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>
        </trim>
        <where>
            <if test="id!=null">
                id = #{id}
            </if>
        </where>
    </update>
   @Test
    void updateByCondition() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(15);
        userInfo.setUserName("李四");
        userInfo.setAge(20);
        userInfo.setGender(1);
        System.out.println(userInfoXMLMapper.updateByCondition(userInfo));
    }

 执行前:

执行后:

上面的SQL语句其实还存在着一定的问题:

假如没有要更新的字段,若再执行这个SQL语句,就会出错。

 可以看到,会多出一个set。

那么应该如何解决呢?需要用到我们的<set>标签。

set

<set>标签可以被用于动态包含需要更新的列,而舍去其他的,例如多余的逗号。

 <update id="updateByCondition">
        update user_info
        <set>
            <if test="userName!=null">
                username = #{userName},
            </if>
            <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="id!=null">
                id = #{id}
            </if>
        </where>
    </update>
@Test
    void updateByCondition() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(15);
        userInfo.setUserName("lisi");
        userInfo.setAge(18);
        System.out.println(userInfoXMLMapper.updateByCondition(userInfo));
    }

删除操作

使用动态SQL来写删除语句,我们可以分为两种,一种是删除一条数据,而另一种则是执行一条SQL语句后要删除多个数据。

那么这里我们就讲删除多个数据的案例,删除单条数据也可以用。

由于我们不知道有多少条数据要被删除,所以我们需要用到 <foreach>标签。

foreach

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

  • collection:绑定方法参数中的集合,如List、Set、Map等;
  • item:遍历时的每一个对象的名称;
  • open:语句块开头的字符串;
  • close:语句块结束的字符串;
  • separator:每次遍历之间间隔的字符串。

    public Integer deleteById(List<Integer> ids);
  @Test
    void deleteById() {
        userInfoXMLMapper.deleteById(List.of(1, 15));
        userInfoXMLMapper.selectAll().forEach(userInfo -> log.info(userInfo.toString()));
    }
   <delete id="deleteById">
        delete from user_info 
        where id in
            <foreach collection="ids" item="id" separator="," open="(" close=")">
                    #{id}
            </foreach>
    </delete>

 此外,还有一个标签,适用于所有的SQL语句。

include

我们在XML文件配置中,有时会存在很多重复的片段,那么这时我们就可以将冗余的代码放到一起,需要的时候调用一下即可,这里就需要用到我们的 <include>标签。

首先我们需要用 <sql>标签将重复的片段进行封装,再通过<include>进行引用

    public List<UserInfo> selectAll2();
 <sql id="BaseColumn">
        id, username, password, age, gender, delete_flag ,create_time ,update_time
    </sql>
 <select id="selectAll2" resultType="com.example.demo.Model.UserInfo" resultMap="BaseMap">
        select
        <include refid="BaseColumn"></include>
        from user_info
    </select>


以上都是利用XML文件来使用动态SQL,那么如果我们想要用注解,就需要在XML文件中编写的语句前后再加上<script></script>

示例:

   @Select("<script> select * from user_info" +
            "        <where>" +
            "            <if test=\"userName!=null\">" +
            "                username = #{userName}\n" +
            "            </if>" +
            "            <if test=\"password!=null\">" +
            "                and password = #{password}" +
            "            </if>" +
            "            <if test=\"phone!=null\">" +
            "                and phone = #{phone}" +
            "            </if>" +
            "            <if test=\"age!=null\">" +
            "                and age = #{age}" +
            "            </if>" +
            "        </where> </script>")
    public List<UserInfo> selectByCondition(UserInfo userInfo);


 

以上就是本篇所有内容~

若有不足,欢迎指正~