MyBatis <where> 标签详解:让你的动态 SQL 更优雅、更安全

MyBatis <where> 标签详解:让你的动态 SQL 更优雅、更安全

一、什么是 <where> 标签?

<where> 是 MyBatis 动态 SQL 中最常用的标签之一。它的核心作用是智能地构建 SQL 查询语句中的 WHERE 子句,主要解决两个痛点:

  1. 自动处理 WHERE 关键字 :如果没有条件,不生成 WHERE;如果有条件,自动添加 WHERE
  2. 自动去除多余的连接词 :自动去掉第一个条件前面多余的 ANDOR

简单来说,<where> 标签让你彻底告别 WHERE 1=1 这种丑陋的写法


二、基本语法

xml 复制代码
<select id="xxx" resultType="xxx">
    SELECT * FROM table_name
    <where>
        <!-- 动态条件 -->
        <if test="条件1">
            AND column1 = #{value1}
        </if>
        <if test="条件2">
            AND column2 = #{value2}
        </if>
    </where>
</select>

2.1 工作原理

场景 SQL 生成结果
没有任何条件成立 SELECT * FROM table_name(无 WHERE)
第一个条件成立 自动去掉开头的 AND,生成 WHERE column1 = ?
多个条件成立 保留第一个条件的 AND,后续正常拼接

三、为什么需要 <where> 标签?

3.1 传统写法(不推荐)

<where> 出现之前,开发者通常使用 WHERE 1=1 来避免 AND 位置问题:

xml 复制代码
<select id="selectByCondition" resultType="car">
    SELECT * FROM t_car
    WHERE 1=1
    <if test="brand != null and brand != ''">
        AND brand = #{brand}
    </if>
    <if test="carType != null and carType != ''">
        AND car_type = #{carType}
    </if>
</select>

问题

  • WHERE 1=1 在数据量大时会影响查询性能(虽然现代数据库优化器会忽略它,但不够优雅)。
  • 可读性差,显得不够专业。

3.2 使用 <where> 标签(推荐)

xml 复制代码
<select id="selectByCondition" resultType="car">
    SELECT * FROM t_car
    <where>
        <if test="brand != null and brand != ''">
            AND brand = #{brand}
        </if>
        <if test="carType != null and carType != ''">
            AND car_type = #{carType}
        </if>
    </where>
</select>

优点

  • 代码更清晰、更专业。
  • 自动处理 WHEREAND,减少手误。
  • 没有多余的 1=1,SQL 更简洁。

四、<where> 标签的核心智能行为

4.1 自动去除开头的 ANDOR

示例:第一个条件成立

xml 复制代码
<where>
    <if test="brand != null">
        AND brand = #{brand}    <!-- 开头的 AND 会被自动去掉 -->
    </if>
</where>

生成结果:WHERE brand = ?(而不是 WHERE AND brand = ?

4.2 没有条件时,不生成 WHERE

xml 复制代码
<where>
    <if test="brand != null">   <!-- 条件为 false -->
        AND brand = #{brand}
    </if>
</where>

生成结果:SELECT * FROM t_car(无 WHERE)

4.3 多个条件

xml 复制代码
<where>
    <if test="brand != null">
        AND brand = #{brand}
    </if>
    <if test="carType != null">
        AND car_type = #{carType}
    </if>
</where>

生成结果:

  • 两个条件都成立:WHERE brand = ? AND car_type = ?
  • 只有第二个成立:WHERE car_type = ?(第一个条件的 AND 不存在,第二个的 AND 被自动去掉)

五、<where><if> 配合使用的最佳实践

5.1 条件判断标准模板

对于字符串类型,务必同时判断 null 和空字符串:

xml 复制代码
<if test="brand != null and brand != ''">
    AND brand = #{brand}
</if>

对于数值类型,只需判断 null

xml 复制代码
<if test="minPrice != null">
    AND guide_price >= #{minPrice}
</if>

5.2 多条件组合示例

xml 复制代码
<select id="selectCars" resultType="car">
    SELECT * FROM t_car
    <where>
        <!-- 品牌模糊查询 -->
        <if test="brand != null and brand != ''">
            AND brand LIKE CONCAT('%', #{brand}, '%')
        </if>
        
        <!-- 价格区间 -->
        <if test="minPrice != null">
            AND guide_price >= #{minPrice}
        </if>
        <if test="maxPrice != null">
            AND guide_price &lt;= #{maxPrice}
        </if>
        
        <!-- 车辆类型 -->
        <if test="carType != null and carType != ''">
            AND car_type = #{carType}
        </if>
        
        <!-- 生产日期范围 -->
        <if test="startDate != null and startDate != ''">
            AND produce_time >= #{startDate}
        </if>
        <if test="endDate != null and endDate != ''">
            AND produce_time &lt;= #{endDate}
        </if>
    </where>
</select>

六、<where> 的底层原理:基于 <trim> 的实现

<where> 标签本质上是一个 <trim> 标签的快捷方式

<where> 等价于:

xml 复制代码
<trim prefix="WHERE" prefixOverrides="AND |OR ">
    <!-- 动态条件 -->
</trim>

参数说明

  • prefix="WHERE":如果内部有内容,前面加上 WHERE
  • prefixOverrides="AND |OR ":去掉内部开头多余的 ANDOR(注意 OR 后面有空格)。

七、常见错误与避坑指南

7.1 ❌ 错误:在 <where> 内部使用 OR 连接词

xml 复制代码
<where>
    <if test="brand != null">
        OR brand = #{brand}   <!-- 第一个条件是 OR,会被去掉,但逻辑可能错误 -->
    </if>
</where>

如果第一个条件以 OR 开头,<where> 会去掉它,但生成的 SQL 可能不符合业务逻辑。建议统一使用 AND,并用括号包裹 OR 逻辑。

7.2 ❌ 错误:条件写在 <where> 标签外部

xml 复制代码
SELECT * FROM t_car
WHERE
<if test="brand != null">
    brand = #{brand}
</if>

如果条件不成立,SQL 会变成 SELECT * FROM t_car WHERE,直接报错。

正确做法 :所有条件必须写在 <where> 标签内部。

7.3 ❌ 错误:忘记判断空字符串

xml 复制代码
<if test="brand != null">
    AND brand = #{brand}
</if>

如果传入 brand = ""(空字符串),条件成立,SQL 变成 AND brand = '',这可能不是你想要的结果。

正确做法 :字符串类型同时判断 null 和空字符串。

7.4 ❌ 错误:<where>WHERE 1=1 混用

xml 复制代码
<where>
    1=1   <!-- 这会导致 WHERE 1=1 始终成立,且无法去除 -->
    <if test="brand != null">
        AND brand = #{brand}
    </if>
</where>

<where> 只处理 AND/OR,不会处理 1=1。混用会导致 SQL 变成 WHERE 1=1 AND brand = ?,虽然能运行,但失去了 <where> 的意义。


八、<where> vs 其他方案对比

方案 SQL 生成 优点 缺点
WHERE 1=1 固定有 WHERE 简单,不会出错 不优雅,影响可读性
手动拼接 WHERE 需手动控制 完全控制 容易出错,代码冗余
<where> 标签 智能生成 优雅、安全、专业 需理解原理
<trim> 自定义 完全自定义 最灵活 配置稍复杂

九、完整实战示例

Mapper 接口

java 复制代码
List<Car> selectByCondition(@Param("brand") String brand,
                            @Param("carType") String carType,
                            @Param("minPrice") Double minPrice,
                            @Param("maxPrice") Double maxPrice);

XML 映射

xml 复制代码
<select id="selectByCondition" resultType="car">
    SELECT * FROM t_car
    <where>
        <if test="brand != null and brand != ''">
            AND brand LIKE CONCAT('%', #{brand}, '%')
        </if>
        <if test="carType != null and carType != ''">
            AND car_type = #{carType}
        </if>
        <if test="minPrice != null">
            AND guide_price >= #{minPrice}
        </if>
        <if test="maxPrice != null">
            AND guide_price &lt;= #{maxPrice}
        </if>
    </where>
</select>

生成的 SQL

  • 传入 brand="宝马"carType=nullminPrice=nullmaxPrice=null

    sql 复制代码
    SELECT * FROM t_car WHERE brand LIKE CONCAT('%', '宝马', '%')
  • 传入 brand="宝马"carType="燃油车"minPrice=10maxPrice=50

    sql 复制代码
    SELECT * FROM t_car WHERE brand LIKE CONCAT('%', '宝马', '%') AND car_type = '燃油车' AND guide_price >= 10 AND guide_price <= 50
  • 所有参数都为 null

    sql 复制代码
    SELECT * FROM t_car

十、总结

  1. <where> 是动态 SQL 的核心标签之一,用于智能构建 WHERE 子句。
  2. 自动处理 WHERE 关键字:有条件加,无条件不加。
  3. 自动去除开头的 AND/OR:避免 SQL 语法错误。
  4. 永远配合 <if> 使用 :条件判断逻辑写在 <if>test 中。
  5. 字符串类型必须判断空字符串!= null and != ''
  6. 底层基于 <trim> 实现:理解其原理有助于应对复杂场景。

<where> 标签让 MyBatis 的动态 SQL 更加优雅和专业。掌握它,你就能告别 WHERE 1=1 的尴尬写法,写出更高质量的代码。

相关推荐
RainCity1 小时前
Java Swing 自定义组件库分享(十六)
java·笔记·后端
沙盘客1 小时前
AFSIM 14篇 C++ 插件开发:扩展你的仿真能力
c++·后端
桦说编程1 小时前
如何对待中断异常:一个被吞掉的 InterruptedException 引发的思考
后端
雪隐3 小时前
个人电脑玩AI-16让5060 Ti给你打工——5060Ti 16G 跑 MiniMax-Music-3:从下载到 60s 出歌的全流程
前端·人工智能·后端
报错小能手3 小时前
Go 语言结构 基础语法
开发语言·后端·golang
暗黑小白4 小时前
参数从哪来、何时来 —— 提取时机与平台化 Slot 管理
人工智能·后端·大模型·ai agent
剩下了什么4 小时前
go语言 Ctx:「错误三:在 Context 中存储可变值」
开发语言·后端·golang
程序员鱼皮4 小时前
DeepSeek Harness 最新邪修曝光!被吹成核弹的极简模式,真的夯吗?
前端·后端·ai编程
Gopher_HBo4 小时前
请求绑定与校验
后端