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 Listlt;Longgt; idList; // 批量ID // getter/setter 省略 }2.1 if 标签基础单条件动态拼接作用判断参数是否非空满足条件则拼接对应SQL最基础、使用频率最高的动态标签。语法规则test属性为OGNL表达式支持非空判断、数值判断、字符串判断。!-- 多条件动态查询用户 -- select idlistUserByCondition resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery SELECT id, username, age, phone, status, create_time FROM user WHERE del_flag 0 !-- 用户名非空则模糊查询 -- if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if !-- 年龄不为空则精准查询 -- if testage ! null AND age #{age} /if !-- 状态不为空则筛选 -- if teststatus ! null AND status #{status} /if /select坑点注意单纯使用if标签首个条件为空时会残留多余的AND导致SQL语法报错需配合where标签使用。2.2 where 标签智能处理查询条件前缀核心优势1. 自动识别并添加where关键字2. 自动去除第一个条件前多余的 and/or3. 无任何条件时不生成where语句。完美解决if标签单独使用的语法报错问题企业开发查询场景必用。select idlistUserByCondition resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery SELECT id, username, age, phone, status, create_time FROM user where del_flag 0 if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if testage ! null AND age #{age} /if if teststatus ! null AND status #{status} /if /where /select2.3 trim 标签自定义语法裁剪万能替代where/set作用自定义拼接前缀、后缀同时裁剪指定的首尾多余字符是动态SQL的万能语法修复标签。核心属性prefix整体拼接的前缀字符串suffix整体拼接的后缀字符串prefixOverrides需要去除的首部字符and/orsuffixOverrides需要去除的尾部字符逗号Demotrim实现where标签效果select idlistUserByTrim resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery SELECT id, username, age, phone, status, create_time FROM user trim prefixWHERE prefixOverridesAND|OR del_flag 0 if testusername ! null and username ! AND username LIKE CONCAT(%, #{username}, %) /if if teststatus ! null AND status #{status} /if /trim /select2.4 set 标签动态更新字段专用场景后台修改用户信息时只更新传入的字段空字段不更新避免覆盖原有数据。核心优势自动去除最后一个字段后的多余逗号彻底解决动态更新的语法报错问题。update idupdateUserDynamic parameterTypecom.xxx.entity.User UPDATE user set if testusername ! null and username ! username #{username}, /if if testage ! null age #{age}, /if if testphone ! null and phone ! phone #{phone}, /if if teststatus ! null status #{status} /if /set WHERE id #{id} AND del_flag 0 /update2.5 choose/when/otherwise互斥分支判断核心特点多条件单选互斥自上而下匹配匹配成功一个分支后不再执行后续分支类似 Java 的if-else if-else。适用场景优先级筛选、唯一条件匹配如优先ID查询无ID则用户名查询最后查全部select idgetUserByPriority resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery SELECT id, username, age, status FROM user where del_flag 0 choose !-- 优先根据ID精准查询 -- when testid ! null AND id #{id} /when !-- ID为空则根据用户名查询 -- when testusername ! null and username ! AND username #{username} /when !-- 所有条件为空默认查询正常状态用户 -- otherwise AND status 1 /otherwise /choose /where /select2.6 foreach 标签批量操作核心高频必考核心场景批量删除、批量新增、IN集合查询、批量更新。核心属性说明属性作用collection遍历集合/数组参数名List填list、数组填array、自定义参数填参数名item遍历后的单个元素别名index遍历下标/Map键名open遍历整体前缀如 IN ( 的左括号close遍历整体后缀如 ) 的右括号separator元素之间的分隔符逗号、OR等Demo1IN 批量查询根据ID集合查询用户select idlistUserByIdList resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery SELECT * FROM user WHERE del_flag 0 if testidList ! null and idList.size() 0 AND id IN foreach collectionidList itemid open( close) separator, #{id} /foreach /if /selectDemo2批量删除用户delete idbatchDeleteUser parameterTypejava.util.List DELETE FROM user WHERE id IN foreach collectionlist itemid open( close) separator, #{id} /foreach /deleteDemo3批量新增用户高性能insert idbatchInsertUser parameterTypejava.util.List INSERT INTO user (username, age, phone, status, create_time) VALUES foreach collectionlist itemuser separator, (#{user.username}, #{user.age}, #{user.phone}, #{user.status}, NOW()) /foreach /insert2.7 bind 标签参数绑定与模糊查询优化作用自定义绑定变量简化OGNL表达式解决数据库模糊查询兼容性问题MySQL、Oracle适配同时预防SQL注入。select idlistUserByLike resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery !-- 绑定模糊查询变量全局复用 -- bind namelikeUsername value% username %/ SELECT * FROM user where del_flag 0 if testusername ! null and username ! AND username LIKE #{likeUsername} /if /where /select2.8 sql includeSQL片段复用作用抽取通用SQL片段公共字段、通用查询条件避免代码冗余统一维护。!-- 抽取公共查询字段 -- sql iduserCommonField id, username, age, phone, status, create_time /sql !-- 抽取通用删除条件 -- sql iddelFlagCondition del_flag 0 /sql select idlistAllUser resultTypecom.xxx.entity.User SELECT include refiduserCommonField/ FROM user where include refiddelFlagCondition/ /where /select三、动态SQL高频场景整合Demo3.1 综合多条件筛选ifwherebind适配用户名模糊、年龄区间、状态筛选全非必填参数select idlistUserComplexQuery resultTypecom.xxx.entity.User parameterTypecom.xxx.dto.UserQuery bind namelikeName value% username %/ SELECT id, username, age, status, create_time FROM user where del_flag 0 if testusername ! null and username ! AND username LIKE #{likeName} /if if testminAge ! null AND age #{minAge} /if if testmaxAge ! null AND age #{maxAge} /if if teststatus ! null AND status #{status} /if /where ORDER BY create_time DESC /select3.2 动态字段更新setif只更新传入的非空字段保留数据库原有旧数据update idupdateUserSelective parameterTypecom.xxx.entity.User UPDATE user set if testusername ! null and username ! username#{username},/if if testage ! nullage#{age},/if if testphone ! null and phone ! phone#{phone},/if if teststatus ! nullstatus#{status},/if update_time NOW() /set WHERE id #{id} AND del_flag 0 /update四、动态SQL核心避坑指南高频报错常见坑点报错原因解决方案多余AND/OR语法错误首个if条件为空残留前置AND所有多条件查询统一使用 where 标签UPDATE末尾多余逗号动态字段最后一条拼接逗号更新语句统一使用 set 标签foreach空集合报错集合为空时生成 IN() 空语法遍历前加判断size0字符串空串判断遗漏只判断null未判断空字符串导致无效查询字符串统一判断str ! null and str ! choose多分支同时生效误用if替代choose未理解互斥逻辑单选条件必须使用choose不可用多个if五、企业开发动态SQL规范总结查询场景多条件非必填查询固定搭配where if杜绝手写where关键字更新场景局部动态更新必须使用set if防止逗号语法错误单选分支优先级、互斥条件强制使用choose/when/otherwise批量操作所有集合遍历必须用foreach且前置非空判断代码复用公共字段、通用条件统一抽取sql片段全局include引用参数判断数值型只判null字符串必须同时判null和空串六、全文总结MyBatis动态SQL的核心价值是适配业务不确定性通过8大核心标签可完美解决多条件筛选、局部更新、批量操作、分支查询等所有复杂业务场景。开发核心口诀查询用where、更新用set、单选用choose、批量用foreach、复用抽sql、空参必判断。掌握本文所有Demo与避坑要点可完全覆盖企业开发中99%的动态SQL开发场景杜绝语法报错与代码冗余问题。