MyBatis 之 动态 SQL(上)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: MyBatis 之 动态 SQL(上)

引言



动态 SQL 主要是用来完成不同条件下的 SQL 拼接的,它可以通过一些例如 if 标签、where 标签等,来直接限制 SQL 语句的条件。


如果说 SQL 是 HTML 代码,那么动态 SQL 的标签就相当于 JS,它可以对 SQL 语句进行变换、更改、限制。


传统 SQL 与 动态 SQL 之间的区别



我们观察下面的 userinfo 表,发现 username 和 password 是必传参数,而 photo 是有默认值的。


c27536bf70a148a3adddfc1c12fb0e19.png


所以,我们预期实现下面的两个测试。


第一个测试:插入 username、password、photo

带一个测试:插入 username 、password


那么,我们预期的第二个测试的 photo 的字段值为默认的 " 123.png ",然而,第一个测试就是我们自己插入而设置的 photo 值。

很显然,如果我们利用 MyBatis 写 SQL 语句,应该如下写:


-- 第一个测试
insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo})
-- 第二个测试
insert into userinfo (username, password) values (#{username}, #{password})


基于上述的过程,我们发现,在传统的 SQL 语句写法上,针对于两次测试,我们必须写两个 SQL 语句。这就带来一个问题,如果客户端需要改变需求了,那么后端程序员就要重写写 SQL,也就要重新改变 xml 文件中的一些配置,这会很麻烦…


但实际上,如果我们利用 if 标签,就能够直接对 photo 这个字段进行限制,从而只需要写一个 SQL 语句即可。根据一个 SQL 语句,再加上一些限制标签,就能够很好地解决相似的问题。


综上所述,我们利用 MyBatis 为我们提供的标签来实现动态 SQL,会带来一个灵活的功能:后端在传 SQL 参数时,能够方便地控制增删改查的条件了。


1. if 标签



主要作用


if 标签常用来判断一个参数是否被需要,如果参数不需要,就会隐藏 SQL.


代码实现


UserInfo 类:


@Data
public class UserInfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
}


" UserMapper " 接口:


@Mapper
public interface UserMapper {
    // 添加新用户,使用动态 SQL 中的 if 标签
    public int addUser2(UserInfo userInfo);
}


xml 文件


<?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.UserMapper">
    <!-- 添加新用户,使用动态 SQL 中的 if 标签 -->
    <!-- insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo})  -->
    <insert id="addUser2">
        insert into userinfo (username, password
        <if test="photo != null">
            ,photo
        </if>
        )values (#{username}, #{password}
        <if test="photo != null">
            ,#{photo}
        </if>
        )
    </insert>
</mapper>


测试类1:


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    // 添加新用户,使用动态 SQL 中的 if 标签
    @Test
    void addUser2() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("JJ");
        userInfo.setPassword("246");
        userInfo.setPhoto("JJ.png");
        int result = userMapper.addUser2(userInfo);
        System.out.println("测试结果: " + result);
    }
}


启动测试方法,查看 MyBatis 日志打印:


0150ad4ad09a4500a9cd273d67ca4fe5.png


观察数据库:


477a4b26595542d69533ff20f764c7a0.png


测试类2:


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    // 添加新用户,使用动态 SQL 中的 if 标签
    @Test
    void addUser2() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("云云");
        userInfo.setPassword("137");
        int result = userMapper.addUser2(userInfo);
        System.out.println("测试结果: " + result);
    }
}


启动测试方法,查看 MyBatis 日志打印:


a4a48a790ae54cad869d412a25e817e5.png


观察数据库:


a5fd0e75c9294e1fb358fcba0cdd06a2.png


总结 if 标签语法


if 标签中的 test 是必传参数,传的是对象的属性。


<if test="">
</if>


如下图所示,if 标签中的值既可以写表的字段,也可以写对象的属性,通常情况下,这两者就是用来相互配合的,我们应该弄清楚两者的区别。


此外,如果真的分不清楚两者的关系,我们就可以将实体类的成员变量和数据表的字段写成一样的名字,这样就可以无差别对待了。


448046b08ff440e79714a501a3da661d.png


2. trim 标签



主要作用


trim 标签一般和 if 标签搭配使用,它能够为 SQL 语句添加或去除某个字符(串)。


代码实现


UserInfo 类:


@Data
public class UserInfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
}


" UserMapper " 接口:


@Mapper
public interface UserMapper {
    // 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签
    public int addUser3(UserInfo userInfo);
}


xml 文件


<?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.UserMapper">
    <!--  添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签 -->
    <!-- insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo})  -->
    <insert id="addUser3">
        insert into userinfo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null">
                username,
            </if>
            <if test="password != null">
                password,
            </if>
            <if test="photo != null">
                photo
            </if>
        </trim>
        values
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null">
                #{username},
            </if>
            <if test="password != null">
                #{password},
            </if>
            <if test="photo != null">
                #{photo}
            </if>
        </trim>
    </insert>
</mapper>


测试类1:


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    // 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签
    @Test
    void addUser3() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("莉莉");
        userInfo.setPassword("632");
        int result = userMapper.addUser3(userInfo);
        System.out.println("测试结果: " + result);
    }
}


启动测试方法,查看 MyBatis 日志打印:


d7af03ef8df14e9491dacd0e0584c721.png


观察数据库:


4213223ad44e4ff4ba5b3ca0c74553ed.png


测试类2:


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    // 添加新用户,使用动态 SQL 中的 if 标签 和 trim 标签
    @Test
    void addUser3() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("杰杰");
        userInfo.setPassword("168");
        userInfo.setPhoto("杰杰.png");
        int result = userMapper.addUser3(userInfo);
        System.out.println("测试结果: " + result);
    }
}


启动测试方法,查看 MyBatis 日志打印:


34cc33b0e48b4a21a03d8f305d023dea.png


观察数据库:


8c04056b361947c3a7e05339521b5074.png


代码分析


测试类1 的 SQL 转换,即去除 " 逗号 " 的过程,如下图所示:


7e5949dd8ab649b88a6a5c4528f54952.png


此外,trim 标签非常智能,它能够自动检测 SQL 语句块的末尾是否真的有 " 逗号 ",例如我们上面的测试类2 中,我们实现了 【 suffixOverrides = “,” 】,但 photo 的末尾是没有 " 逗号 "的,也不会报错。如下所示:


insert into userinfo (username, password, photo) values (#{username}, #{password}, #{photo})


总结 trim 标签语法


trim 标签有四个属性,它们可以达到往 SQL 语句块中拼接或删除字符(串)的作用,从而控制 SQL 的写法。四个属性不是必须的,可以根据语法自行设置。


① prefix:需要拼接的前缀字符串

② suffix:需要拼接的后缀字符串

③ prefixOverrides:需要删除的前缀字符串

④ suffixOverrides:需要删除的后缀字符串


3. where 标签



主要作用


where 标签一般与 if 标签搭配使用,它可以直接代替 SQL 语句中的 where 关键字,在 where 标签中,我们可以控制条件的数量,从而达到不同的 select 查询要求。


代码实现


UserInfo 类:


@Data
public class UserInfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
}


" UserMapper " 接口:


@Mapper
public interface UserMapper {
    // 查询用户使用 where 标签
    public List<UserInfo> getUserById2(@Param("id") Integer id);
}


xml 文件


<?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.UserMapper">
    <!--  根据用户 id 来查询某个用户的所有信息 -->
    <!-- select * from userinfo where id = #{id} -->
    <select id="getUserById2" resultType="com.example.demo.model.UserInfo">
        select * from userinfo
        <where>
            <if test="id != null">
                id = #{id}
            </if>
        </where>
    </select>
</mapper>


测试类1:


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    // 查询用户使用 where 标签
    @Test
    void getUserById2() {
        List<UserInfo> list = userMapper.getUserById2(2);
        System.out.println("测试结果:" + list);
    }
}


启动测试方法,查看 MyBatis 日志打印:


78538c68aa734738a53860fc9fcc38b8.png


观察数据库:


8b71b20a088444ee949c16bb0f1e4381.png


测试类2:


@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;
    // 查询用户使用 where 标签
    @Test
    void getUserById2() {
        List<UserInfo> list = userMapper.getUserById2(null);
        System.out.println("测试结果:" + list);
    }
}


启动测试方法,查看 MyBatis 日志打印:


image.png


观察数据库:


fdb54cb213d843e0acc80c62cc4a160d.png


总结 where 标签语法


如果 where 标签中的有值,那么就会正常执行 where 后面的 SQL 语句,如果 where 标签中没有值,那么整个 where 语句也会被省略。所以一般情况下,where 被省略了,就会变成一个全列查询。


此外,where 标签也等价于下面的写法,实际上它能省略语句中的前缀 and,但是它省略不了语句中的后缀 and.


<trim prefix="where" prefixOverrides="and">
</trim>


基于上述的 where 语法,形如下面的代码,一个 SQL 语句中,我们就可以变幻出四种语法。


select * from userinfo
<where>
    <if test="username!= null">
        username= #{usernname}
    </if>
    <if test="password!= null">
        and password= #{password}
    </if>
</where>


① 只查询 username

② 只查询 password

③ 既查询 username,也查询 password

④ 全列查询


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
4月前
|
SQL Java 测试技术
3、Mybatis-Plus 自定义sql语句
这篇文章介绍了如何在Mybatis-Plus框架中使用自定义SQL语句进行数据库操作。内容包括文档结构、编写mapper文件、mapper.xml文件的解释说明、在mapper接口中定义方法、在mapper.xml文件中实现接口方法的SQL语句,以及如何在单元测试中测试自定义的SQL语句,并展示了测试结果。
3、Mybatis-Plus 自定义sql语句
|
1月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
2月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
45 10
|
3月前
|
SQL XML Java
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
文章介绍了MyBatis中动态SQL的用法,包括if、choose、where、set和trim标签,以及foreach标签的详细使用。通过实际代码示例,展示了如何根据条件动态构建查询、更新和批量插入操作的SQL语句。
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
|
4月前
|
SQL Java 数据库连接
Mybatis系列之 Error parsing SQL Mapper Configuration. Could not find resource com/zyz/mybatis/mapper/
文章讲述了在使用Mybatis时遇到的资源文件找不到的问题,并提供了通过修改Maven配置来解决资源文件编译到target目录下的方法。
Mybatis系列之 Error parsing SQL Mapper Configuration. Could not find resource com/zyz/mybatis/mapper/
|
3月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
58 1
|
4月前
|
SQL Java 数据库连接
Mybatis系列之 动态SQL
文章详细介绍了Mybatis中的动态SQL用法,包括`<if>`、`<choose>`、`<when>`、`<otherwise>`、`<trim>`和`<foreach>`等元素的应用,并通过实际代码示例展示了如何根据不同条件动态生成SQL语句。
|
4月前
|
SQL 关系型数据库 MySQL
解决:Mybatis-plus向数据库插入数据的时候 报You have an error in your SQL syntax
该博客文章讨论了在使用Mybatis-Plus向数据库插入数据时遇到的一个常见问题:SQL语法错误。作者发现错误是由于数据库字段中使用了MySQL的关键字,导致SQL语句执行失败。解决方法是将这些关键字替换为其他字段名称,以避免语法错误。文章通过截图展示了具体的操作步骤。
|
4月前
|
SQL Java 关系型数据库
SpringBoot 系列之 MyBatis输出SQL日志
这篇文章介绍了如何在SpringBoot项目中通过MyBatis配置输出SQL日志,具体方法是在`application.yml`或`application.properties`中设置MyBatis的日志实现为`org.apache.ibatis.logging.stdout.StdOutImpl`来直接在控制台打印SQL日志。
SpringBoot 系列之 MyBatis输出SQL日志
|
5月前
|
SQL Java 数据库连接
idea中配置mybatis 映射文件模版及 mybatis plus 自定义sql
idea中配置mybatis 映射文件模版及 mybatis plus 自定义sql
98 3