news 2026/7/31 16:43:39

MyBatis:动态 SQL 全景梳理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MyBatis:动态 SQL 全景梳理

在实际业务开发中,数据库查询条件几乎不会一成不变:前端多条件筛选、非必填参数、动态排序、分页拼接、批量增删改等场景,固定写死的SQL语句完全无法适配业务需求。

MyBatis 动态SQL是其核心核心能力之一,区别于静态SQL,可根据参数是否为空、参数值、业务状态自动拼接、裁剪、优化SQL语句,彻底解决多条件动态查询、批量操作、条件分支适配问题。

本文将从核心原理、全套标签详解、场景Demo、对比表格、实战规范、常见坑点全方位梳理,覆盖99%企业开发动态SQL场景。

一、动态SQL核心原理与优势

1.1 核心原理

MyBatis 在执行SQL前,会通过OGNL表达式解析XML中的动态标签,根据传入的参数对象属性值,动态判断是否拼接SQL片段,最终生成一条合规、无语法错误的可执行SQL,再交由数据库执行。

1.2 动态SQL vs 静态SQL 对比

对比维度

静态SQL

动态SQL

写法特点

SQL语句固定写死,无逻辑判断

通过标签+表达式动态拼接SQL片段

参数适配性

参数缺失会报SQL语法错误、查询异常

自动忽略空参数,适配非必填条件

业务场景

仅适用于固定条件查询、简单CRUD

多条件筛选、批量操作、动态排序/分页

维护成本

多场景需写多条SQL,冗余极高

一条SQL适配全场景,统一维护

语法安全性

易出现多余and/or、逗号等语法问题

内置语法裁剪机制,杜绝语法错误

1.3 动态SQL全套核心标签总览

标签

核心作用

适用场景

if

单条件判断,满足条件则拼接SQL

非必填查询条件、参数动态拼接

where

自动去除多余的 and/or,智能拼接where关键字

多if条件组合查询,解决语法冗余问题

trim

自定义前后缀、去除指定冗余字符,高度灵活

where/set无法满足的特殊裁剪场景

set

动态拼接update字段,自动去除末尾多余逗号

动态更新字段(部分字段更新)

choose/when/otherwise

多条件互斥分支,只执行一个匹配条件

单选条件筛选(优先级判断)

foreach

遍历集合、数组,批量拼接SQL片段

批量查询、批量新增、批量删除

bind

绑定自定义变量,简化表达式、防止SQL注入

模糊查询、复杂参数处理

include/sql

抽取通用SQL片段,复用代码

公共字段、通用查询条件复用

二、全标签深度解析 + 可直接运行Demo

统一前置实体与参数:本文所有Demo基于User 实体类user 数据表,适配SpringBoot + MyBatis 常规项目。

// User实体类 public class User { private Long id; private String username; private Integer age; private String phone; private Integer status; // 状态 0-禁用 1-正常 private LocalDateTime createTime; // getter/setter 省略 } // 前端查询参数DTO(动态查询入参) public class UserQuery { private String username; // 模糊查询 private Integer age; // 精准查询 private Integer status; // 状态筛选 private List<Long> idList; // 批量ID // getter/setter 省略 }

2.1 if 标签:基础单条件动态拼接

作用:判断参数是否非空,满足条件则拼接对应SQL,最基础、使用频率最高的动态标签。

语法规则:test属性为OGNL表达式,支持非空判断、数值判断、字符串判断。

<!-- 多条件动态查询用户 --> <select id="listUserByCondition" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> SELECT id, username, age, phone, status, create_time FROM user WHERE del_flag = 0 <!-- 用户名非空则模糊查询 --> <if test="username != null and username != ''"> AND username LIKE CONCAT('%', #{username}, '%') </if> <!-- 年龄不为空则精准查询 --> <if test="age != null"> AND age = #{age} </if> <!-- 状态不为空则筛选 --> <if test="status != null"> AND status = #{status} </if> </select>

坑点注意:单纯使用if标签,首个条件为空时,会残留多余的AND,导致SQL语法报错,需配合where标签使用。

2.2 where 标签:智能处理查询条件前缀

核心优势:1. 自动识别并添加where关键字;2. 自动去除第一个条件前多余的 and/or;3. 无任何条件时,不生成where语句。

完美解决if标签单独使用的语法报错问题,企业开发查询场景必用

<select id="listUserByCondition" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> SELECT id, username, age, phone, status, create_time FROM user <where> del_flag = 0 <if test="username != null and username != ''"> AND username LIKE CONCAT('%', #{username}, '%') </if> <if test="age != null"> AND age = #{age} </if> <if test="status != null"> AND status = #{status} </if> </where> </select>

2.3 trim 标签:自定义语法裁剪(万能替代where/set)

作用:自定义拼接前缀、后缀,同时裁剪指定的首尾多余字符,是动态SQL的万能语法修复标签。

核心属性

  • prefix:整体拼接的前缀字符串

  • suffix:整体拼接的后缀字符串

  • prefixOverrides:需要去除的首部字符(and/or)

  • suffixOverrides:需要去除的尾部字符(逗号)

Demo:trim实现where标签效果

<select id="listUserByTrim" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> SELECT id, username, age, phone, status, create_time FROM user <trim prefix="WHERE" prefixOverrides="AND|OR"> del_flag = 0 <if test="username != null and username != ''"> AND username LIKE CONCAT('%', #{username}, '%') </if> <if test="status != null"> AND status = #{status} </if> </trim> </select>

2.4 set 标签:动态更新字段专用

场景:后台修改用户信息时,只更新传入的字段,空字段不更新,避免覆盖原有数据。

核心优势:自动去除最后一个字段后的多余逗号,彻底解决动态更新的语法报错问题。

<update id="updateUserDynamic" parameterType="com.xxx.entity.User"> UPDATE user <set> <if test="username != null and username != ''"> username = #{username}, </if> <if test="age != null"> age = #{age}, </if> <if test="phone != null and phone != ''"> phone = #{phone}, </if> <if test="status != null"> status = #{status} </if> </set> WHERE id = #{id} AND del_flag = 0 </update>

2.5 choose/when/otherwise:互斥分支判断

核心特点:多条件单选互斥,自上而下匹配,匹配成功一个分支后,不再执行后续分支,类似 Java 的if-else if-else

适用场景:优先级筛选、唯一条件匹配(如:优先ID查询,无ID则用户名查询,最后查全部)

<select id="getUserByPriority" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> SELECT id, username, age, status FROM user <where> del_flag = 0 <choose> <!-- 优先根据ID精准查询 --> <when test="id != null"> AND id = #{id} </when> <!-- ID为空则根据用户名查询 --> <when test="username != null and username != ''"> AND username = #{username} </when> <!-- 所有条件为空,默认查询正常状态用户 --> <otherwise> AND status = 1 </otherwise> </choose> </where> </select>

2.6 foreach 标签:批量操作核心(高频必考)

核心场景:批量删除、批量新增、IN集合查询、批量更新。

核心属性说明

属性

作用

collection

遍历集合/数组参数名(List填list、数组填array、自定义参数填参数名)

item

遍历后的单个元素别名

index

遍历下标/Map键名

open

遍历整体前缀(如 IN ( 的左括号)

close

遍历整体后缀(如 ) 的右括号)

separator

元素之间的分隔符(逗号、OR等)

Demo1:IN 批量查询(根据ID集合查询用户)

<select id="listUserByIdList" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> SELECT * FROM user WHERE del_flag = 0 <if test="idList != null and idList.size() > 0"> AND id IN <foreach collection="idList" item="id" open="(" close=")" separator=","> #{id} </foreach> </if> </select>

Demo2:批量删除用户

<delete id="batchDeleteUser" parameterType="java.util.List"> DELETE FROM user WHERE id IN <foreach collection="list" item="id" open="(" close=")" separator=","> #{id} </foreach> </delete>

Demo3:批量新增用户(高性能)

<insert id="batchInsertUser" parameterType="java.util.List"> INSERT INTO user (username, age, phone, status, create_time) VALUES <foreach collection="list" item="user" separator=","> (#{user.username}, #{user.age}, #{user.phone}, #{user.status}, NOW()) </foreach> </insert>

2.7 bind 标签:参数绑定与模糊查询优化

作用:自定义绑定变量,简化OGNL表达式,解决数据库模糊查询兼容性问题(MySQL、Oracle适配),同时预防SQL注入。

<select id="listUserByLike" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> <!-- 绑定模糊查询变量,全局复用 --> <bind name="likeUsername" value="'%' + username + '%'"/> SELECT * FROM user <where> del_flag = 0 <if test="username != null and username != ''"> AND username LIKE #{likeUsername} </if> </where> </select>

2.8 sql + include:SQL片段复用

作用:抽取通用SQL片段(公共字段、通用查询条件),避免代码冗余,统一维护。

<!-- 抽取公共查询字段 --> <sql id="userCommonField"> id, username, age, phone, status, create_time </sql> <!-- 抽取通用删除条件 --> <sql id="delFlagCondition"> del_flag = 0 </sql> <select id="listAllUser" resultType="com.xxx.entity.User"> SELECT <include refid="userCommonField"/> FROM user <where> <include refid="delFlagCondition"/> </where> </select>

三、动态SQL高频场景整合Demo

3.1 综合多条件筛选(if+where+bind)

适配:用户名模糊、年龄区间、状态筛选,全非必填参数

<select id="listUserComplexQuery" resultType="com.xxx.entity.User" parameterType="com.xxx.dto.UserQuery"> <bind name="likeName" value="'%' + username + '%'"/> SELECT id, username, age, status, create_time FROM user <where> del_flag = 0 <if test="username != null and username != ''"> AND username LIKE #{likeName} </if> <if test="minAge != null"> AND age >= #{minAge} </if> <if test="maxAge != null"> AND age <= #{maxAge} </if> <if test="status != null"> AND status = #{status} </if> </where> ORDER BY create_time DESC </select>

3.2 动态字段更新(set+if)

只更新传入的非空字段,保留数据库原有旧数据

<update id="updateUserSelective" parameterType="com.xxx.entity.User"> UPDATE user <set> <if test="username != null and username != ''">username=#{username},</if> <if test="age != null">age=#{age},</if> <if test="phone != null and phone != ''">phone=#{phone},</if> <if test="status != null">status=#{status},</if> update_time = NOW() </set> WHERE id = #{id} AND del_flag = 0 </update>

四、动态SQL核心避坑指南(高频报错)

常见坑点

报错原因

解决方案

多余AND/OR语法错误

首个if条件为空,残留前置AND

所有多条件查询统一使用 <where> 标签

UPDATE末尾多余逗号

动态字段最后一条拼接逗号

更新语句统一使用 <set> 标签

foreach空集合报错

集合为空时生成 IN() 空语法

遍历前加判断:size>0

字符串空串判断遗漏

只判断null,未判断空字符串,导致无效查询

字符串统一判断:str != null and str != ''

choose多分支同时生效

误用if替代choose,未理解互斥逻辑

单选条件必须使用choose,不可用多个if

五、企业开发动态SQL规范总结

  1. 查询场景:多条件非必填查询,固定搭配where + if,杜绝手写where关键字

  2. 更新场景:局部动态更新,必须使用set + if,防止逗号语法错误

  3. 单选分支:优先级、互斥条件,强制使用choose/when/otherwise

  4. 批量操作:所有集合遍历必须用foreach,且前置非空判断

  5. 代码复用:公共字段、通用条件统一抽取sql片段,全局include引用

  6. 参数判断:数值型只判null,字符串必须同时判null和空串

六、全文总结

MyBatis动态SQL的核心价值是适配业务不确定性,通过8大核心标签,可完美解决:多条件筛选、局部更新、批量操作、分支查询等所有复杂业务场景。

开发核心口诀:查询用where、更新用set、单选用choose、批量用foreach、复用抽sql、空参必判断

掌握本文所有Demo与避坑要点,可完全覆盖企业开发中99%的动态SQL开发场景,杜绝语法报错与代码冗余问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/31 16:41:57

Shell脚本入门:从零到自动化,告别重复命令操作

1. 从“手忙脚乱”到“一键搞定”&#xff1a;为什么你需要Shell脚本 如果你在Linux或macOS的终端里&#xff0c;曾经为了完成一个任务&#xff0c;把同一串命令敲了十遍&#xff1b;或者&#xff0c;你需要在Windows的PowerShell里&#xff0c;每天重复执行一系列固定的文件整…

作者头像 李华
网站建设 2026/7/31 16:40:45

Avalonia UI Image控件深度解析:从核心原理到跨平台实战优化

1. 项目概述&#xff1a;Avalonia UI中的Image控件在桌面应用开发领域&#xff0c;跨平台UI框架Avalonia UI正以其现代化的架构和出色的性能吸引着越来越多的开发者。无论是开发运行在Windows、macOS、Linux&#xff0c;还是国产操作系统上的应用&#xff0c;Avalonia都提供了统…

作者头像 李华
网站建设 2026/7/31 16:39:32

【单片机毕业设计】基于 STM32 的多档位洗衣模拟控制平台搭建 基于嵌入式开发板的洗衣时序模拟系统实现(015701)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/7/31 16:39:13

【单片机毕业设计】基于 STM32 单片机的蜂鸣提醒式智能电饭煲开发 基于嵌入式技术的模拟电饭煲多模式温控系统设计(016001)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华