MyBatis 动态 SQL的使用场景与实现方法

 更新时间:2026年05月28日 09:40:23   作者:一条泥憨鱼  
这段文章详细介绍了MyBatis动态SQL的使用场景与实现方法,通过具体案例深入浅出地地解动态SQL在企业级开发中的的广泛应用与重要性,感兴趣的朋友跟随小编一起看看吧

前言:

在学习 JavaWeb 或企业级开发时,很多同学都会遇到一个问题:

“查询条件不固定,SQL 语句到底该怎么写?”

比如:

  • 用户可能输入用户名,也可能不输入
  • 商品查询可能按价格、分类、库存组合搜索
  • 更新用户信息时,有些字段可能为空

如果全靠字符串拼接 SQL,不但代码难看,而且容易出错。

于是,MyBatis 提供了一个非常强大的功能:

动态 SQL

它可以让 SQL “像程序一样”动态变化。

这篇文章会带你从零开始,彻底搞懂 MyBatis 动态 SQL。

一、什么是动态 SQL?

先看一个普通 SQL:

SELECT * FROM user WHERE username = 'Tom';

但现实中,用户名可能为空。

那么 SQL 就需要动态变化:

SELECT * FROM user;

或者:

SELECT * FROM user WHERE username = 'Tom';

这就是:SQL 根据条件动态变化

MyBatis 提供了很多标签帮助我们实现这种功能。

最常用的有:

标签作用
<if>条件判断
<where>自动处理 WHERE
<set>自动处理 UPDATE
<foreach>循环遍历
<choose>类似 switch
<trim>自定义拼接规则

二、为什么需要动态 SQL?

如果不用动态 SQL,代码会非常麻烦。

例如:

String sql = "select * from user where 1=1";
if(username != null){
    sql += " and username = #{username}";
}
if(age != null){
    sql += " and age = #{age}";
}

问题很多:

  • 可读性差
  • SQL 拼接容易出错
  • 不方便维护
  • 容易产生 SQL 注入风险

MyBatis 动态 SQL 可以优雅解决这些问题。

三、准备工作

假设有一张用户表:

CREATE TABLE user(
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    age INT,
    gender VARCHAR(10)
);

对应实体类:

public class User {
    private Integer id;
    private String username;
    private Integer age;
    private String gender;
    // getter/setter
}

Mapper接口:

public interface UserMapper {
    List<User> select(User user);
}

四、<if>标签

这是最常用的动态 SQL 标签。

作用:

条件成立时才拼接 SQL

1、基本使用

<select id="select" resultType="User">
    SELECT * FROM user
    WHERE 1=1
    <if test="username != null">
        AND username = #{username}
    </if>
    <if test="age != null">
        AND age = #{age}
    </if>
</select>

2、执行效果

情况1:

User user = new User();
user.setUsername("Tom");

生成 SQL:

SELECT * FROM user
WHERE 1=1
AND username = 'Tom'

情况2:

User user = new User();
user.setAge(20);

生成 SQL:

SELECT * FROM user
WHERE 1=1
AND age = 20

五、<where>标签

很多初学者都会写:

WHERE 1=1

虽然能用,但不优雅。

MyBatis 提供了:

<where>

它能自动:

  • 添加 WHERE
  • 去掉多余的 AND

1、改造代码

<select id="select" resultType="User">
    SELECT * FROM user
    <where>
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>

2、效果

如果没有任何条件:

SELECT * FROM user

如果有条件:

SELECT * FROM user
WHERE username = 'Tom'

是不是优雅很多?

六、<set>标签

用于动态更新 SQL

1、需求

用户修改信息时:

  • 只修改传入的字段
  • 空字段不更新

2、代码实现

<update id="updateUser">
    UPDATE user
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="gender != null">
            gender = #{gender},
        </if>
    </set>
    WHERE id = #{id}
</update>

3、作用

<set> 会自动:

  • 添加 SET
  • 去掉最后一个逗号

4、生成 SQL

UPDATE user
SET username = 'Tom',
    age = 20
WHERE id = 1

七、<foreach>标签

这是开发中非常重要的标签。

作用:循环遍历集合

最经典的场景:批量查询

1、需求

查询多个 ID:

SELECT * FROM user WHERE id IN (1,2,3)

2、Mapper接口

List<User> selectByIds(List<Integer> ids);

3、XML写法

<select id="selectByIds" resultType="User">
    SELECT * FROM user
    <where>
        <foreach collection="list"
                 item="id"
                 open="id IN ("
                 separator=","
                 close=")">
            #{id}
        </foreach>
    </where>
</select>

4、参数解释

属性作用
collection集合名称
item每次遍历元素
open开始字符串
separator分隔符
close结束字符串

5、生成 SQL

SELECT * FROM user
WHERE id IN (1,2,3)

八、<choose>标签

类似 Java 中的:

switch-case

作用:多选一

示例

<select id="select" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="username != null">
                username = #{username}
            </when>
            <when test="age != null">
                age = #{age}
            </when>
            <otherwise>
                gender = '男'
            </otherwise>
        </choose>
    </where>
</select>

逻辑

  • username 有值 → 按 username 查询
  • 否则 age 有值 → 按 age 查询
  • 都没有 → 默认查询 gender='男'

九、<trim>标签

这是一个高级标签。

作用:自定义字符串拼接规则

示例

<trim prefix="WHERE" prefixOverrides="AND |OR ">
    <if test="username != null">
        AND username = #{username}
    </if>
    <if test="age != null">
        AND age = #{age}
    </if>
</trim>

含义

属性作用
prefix添加前缀
prefixOverrides删除前缀
suffix添加后缀
suffixOverrides删除后缀

实际上:

<where>

底层本质上就是:

<trim>

的封装。

十、动态 SQL 执行流程

很多同学会问:

MyBatis 是怎么让 SQL 动态变化的?

流程如下:

XML动态SQL
    ↓
MyBatis解析标签
    ↓
拼接最终SQL
    ↓
预编译SQL
    ↓
执行数据库操作

所以:

动态 SQL 本质上是 SQL 的动态拼接

但它比手写字符串安全得多。

十一、动态 SQL 的开发技巧

1、优先使用<where>

不要再:

WHERE 1=1

了。

2、更新操作使用<set>

避免:

age = 20,

最后多逗号报错。

3、批量操作一定学会<foreach>

企业开发中极其常见。

例如:

  • 批量删除
  • 批量查询
  • 批量插入

4、避免 SQL 过于复杂

动态 SQL 很强大,但不要把所有业务逻辑都写进 SQL

否则后期维护会非常痛苦。

十二、完整案例:多条件查询

Mapper接口

List<User> selectCondition(User user);

XML

<select id="selectCondition" resultType="User">
    SELECT * FROM user
    <where>
        <if test="username != null and username != ''">
            AND username LIKE CONCAT('%',#{username},'%')
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="gender != null">
            AND gender = #{gender}
        </if>
    </where>
</select>

测试代码

User user = new User();
user.setUsername("o");
user.setGender("男");
List<User> users = mapper.selectCondition(user);
users.forEach(System.out::println);

生成 SQL

SELECT * FROM user
WHERE username LIKE '%o%'
AND gender = '男'

十三、动态 SQL 常见面试题

1、动态 SQL 有哪些标签?

常见:

  • <if>
  • <where>
  • <set>
  • <foreach>
  • <choose>
  • <trim>

2、<where>有什么作用?

自动:

  • 添加 WHERE
  • 删除多余 AND/OR

3、<set>有什么作用?

自动:

  • 添加 SET
  • 删除最后逗号

4、#{}和${}区别?

#{}

预编译:

WHERE id = ?

安全,推荐使用。

${}

字符串拼接:

ORDER BY age

存在 SQL 注入风险。

十四、总结

动态 SQL 是 MyBatis 中最核心的能力之一。

掌握后你就能真正写出:

  • 灵活的查询
  • 动态更新
  • 批量操作
  • 企业级 SQL

重点回顾

标签核心作用
<if>条件判断
<where>自动处理 WHERE
<set>动态更新
<foreach>循环集合
<choose>多选一
<trim>自定义拼接

很多初学者觉得:

“动态 SQL 好复杂。”

“根据条件,动态拼接 SQL。”

只要理解这一点,你就已经入门了。

到此这篇关于MyBatis 动态 SQL的使用场景与实现方法的文章就介绍到这了,更多相关MyBatis 动态 SQL内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot整合token的实现代码

    springboot整合token的实现代码

    这篇文章主要介绍了springboot整合token的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • Java编程实现五子棋人人对战代码示例

    Java编程实现五子棋人人对战代码示例

    这篇文章主要介绍了Java编程实现五子棋人人对战代码示例,具有一定借鉴价值,需要的朋友可以参考下。
    2017-11-11
  • 详解Mybatis中的CRUD

    详解Mybatis中的CRUD

    这篇文章主要介绍了Mybatis中的CRUD的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Spring Security更换加密方式的完整方案

    Spring Security更换加密方式的完整方案

    随着现在对软件系统安全性要求的越来越严苛,对于很多项目,升级密码都是势在必行的,若项目使用Spring Security框架,只需稍作修改,便可达到更换加密方式,所以本文给大家介绍了Spring Security更换加密方式的完整方案,需要的朋友可以参考下
    2025-12-12
  • Java编写实现坦克大战小游戏

    Java编写实现坦克大战小游戏

    这篇文章主要为大家详细介绍了Java编写实现坦克大战小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 关于intellij idea打开就闪退或关闭详细解决办法

    关于intellij idea打开就闪退或关闭详细解决办法

    这篇文章主要介绍了关于intellij idea打开就闪退或关闭详细解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Java注解中@Component和@Bean的区别

    Java注解中@Component和@Bean的区别

    这篇文章主要介绍了@Component和@Bean的区别,在这给大家简单介绍下作用对象不同:@Component 注解作用于类,而 @Bean 注解作用于方法,具体实例代码参考下本文
    2024-03-03
  • Java使用Maven BOM统一管理版本号的实现

    Java使用Maven BOM统一管理版本号的实现

    这篇文章主要介绍了Java使用Maven BOM统一管理版本号的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Java动态规划之丑数问题实例讲解

    Java动态规划之丑数问题实例讲解

    这篇文章主要介绍了Java动态规划之丑数问题实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-09-09
  • springboot全局异常处理代码实例

    springboot全局异常处理代码实例

    这篇文章主要介绍了springboot全局异常处理代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01

最新评论