详解MyBatis 动态 SQL

🌈个人主页 :一条泥憨鱼 (欢迎各位大佬莅临)

🎬精选专栏:数据结构与算法JavaSE,苍穹外卖日记AI学习

前言:

在学习 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,代码会非常麻烦。

例如:

java 复制代码
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 可以优雅解决这些问题。


三、准备工作

假设有一张用户表:

sql 复制代码
CREATE TABLE user(
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    age INT,
    gender VARCHAR(10)
);

对应实体类:

java 复制代码
public class User {

    private Integer id;
    private String username;
    private Integer age;
    private String gender;

    // getter/setter
}

Mapper接口:

java 复制代码
public interface UserMapper {

    List<User> select(User user);

}

四、<if> 标签

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

作用:

条件成立时才拼接 SQL


1、基本使用

sql 复制代码
<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:

java 复制代码
User user = new User();
user.setUsername("Tom");

生成 SQL:

java 复制代码
SELECT * FROM user
WHERE 1=1
AND username = 'Tom'

情况2:

java 复制代码
User user = new User();
user.setAge(20);

生成 SQL:

java 复制代码
SELECT * FROM user
WHERE 1=1
AND age = 20

五、<where> 标签

很多初学者都会写:

复制代码
WHERE 1=1

虽然能用,但不优雅。

MyBatis 提供了:

<where>

它能自动:

  • 添加 WHERE

  • 去掉多余的 AND


1、改造代码

sql 复制代码
<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

如果有条件:

sql 复制代码
SELECT * FROM user
WHERE username = 'Tom'

是不是优雅很多?


六、<set> 标签

用于动态更新 SQL


1、需求

用户修改信息时:

  • 只修改传入的字段

  • 空字段不更新


2、代码实现

sql 复制代码
<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

sql 复制代码
UPDATE user
SET username = 'Tom',
    age = 20
WHERE id = 1

七、<foreach> 标签

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

作用:循环遍历集合

最经典的场景:批量查询


1、需求

查询多个 ID:

sql 复制代码
SELECT * FROM user WHERE id IN (1,2,3)

2、Mapper接口

java 复制代码
List<User> selectByIds(List<Integer> ids);

3、XML写法

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

sql 复制代码
SELECT * FROM user
WHERE id IN (1,2,3)

八、<choose> 标签

类似 Java 中的:

复制代码
switch-case

作用:多选一


示例

sql 复制代码
<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> 标签

这是一个高级标签。

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


示例

sql 复制代码
<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接口

java 复制代码
List<User> selectCondition(User user);

XML

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>

测试代码

java 复制代码
User user = new User();

user.setUsername("o");
user.setGender("男");

List<User> users = mapper.selectCondition(user);

users.forEach(System.out::println);

生成 SQL

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。"

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

今天的学习就暂时告一段落啦,如果文章对您有用的话,还请留下一个免费的小心心和关注哦!

祝您工作顺利,生活愉快。我们下期再见!

相关推荐
青枣八神2 小时前
Trae IDE 终端 JDK 版本与系统不一致的解决方案
java·开发语言·ide
寻道码路5 小时前
LangChain4j Java AI 应用开发实战(四):提示词工程进阶 - 模板化与结构化 Prompt 设计
java·人工智能·ai·prompt·aigc
lcreek10 小时前
Java 反序列化漏洞深度解析(一):从URLDNS到真正的DNS探测
java·反序列化漏洞
杰克尼10 小时前
天机学堂复习总结(day03-day04)
java·开发语言·redis·elasticsearch·spring cloud
x***r15111 小时前
jdk-11.0.16.1_windows使用步骤详解(附JDK 11环境变量配置与验证教程)
java·开发语言·windows
弹简特12 小时前
【Java项目-轻聊】01-项目演示+项目介绍+准备工作+项目源码
java
luck_bor12 小时前
File类&递归作业
java·开发语言
weixin_3975740912 小时前
用自然语言查数据库出图表靠谱吗?一次智能问数实践复盘
数据库
武子康12 小时前
Java-07 深入浅出 MyBatis数据库一对多关系模型实战:表结构设计与查询实现
java·后端