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

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

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

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

相关推荐
wang09076 小时前
自己动手写一个spring之MVC_3
java·spring·mvc
SelectDB技术团队6 小时前
Agent 场景动态 JSON 性能拆解:Apache Doris 比 ClickHouse 快 7 倍、比 Elasticsearch 快 2 倍
数据库·clickhouse·elasticsearch·json·apache·日志分析·apache doris
greasyfork6 小时前
多种数据库管理混乱?Navicat Premium 17 for Mac 统一解决
数据库·mysql·macos·mac
闲猫6 小时前
Spring AI / Models / Chat Models / Ollama
java·人工智能·spring
FREEDOM_X6 小时前
Linux 多线程编程——总结
java·linux·运维·ubuntu
Dream_fly_iboy6 小时前
系统架构设计师之数据库
数据库
tmlx3I0817 小时前
Tomcat的架构设计和启动过程详解
java·tomcat
weixin_422329317 小时前
AgentScope Java 项目入门 & Builder 深度解读
java·开发语言·人工智能
shepherd1267 小时前
一次把 Spring MVC 文件上传参数“查没了”的排查:multipart、Filter 与 request body 的连环坑
java·后端·spring·mvc
风和先行7 小时前
Android 数据库相关学习总结
android·数据库·学习