MyBatis <bind> 使用指南

📅 2026/7/5 3:40:16
MyBatis <bind> 使用指南
1. 什么是bindbind是 MyBatis 动态 SQL 中用于定义临时变量的标签。它可以把一个表达式、参数路径或加工后的值先绑定成一个新的变量名然后在后续 SQL 中复用。简单理解bind就是给 MyBatis 动态 SQL 里的某个值起一个临时名字。示例bindnamekeywordLikevalue% keyword %/select * from user_info where user_name like #{keywordLike}上面代码中keywordLike是通过bind生成的临时变量。如果入参是keyword Tom那么最终keywordLike的值就是%Tom%2. 为什么要用bindbind常见用途主要有三个简化复杂表达式统一不同参数路径配合公共 SQL 片段复用3. 常见用法一模糊查询如果直接写模糊查询可能会这样写select * from user_info where user_name like concat(%, #{keyword}, %)这种写法依赖数据库函数不同数据库的函数可能不完全一致。使用bind可以先把模糊查询参数拼好selectidqueryUserListresultTypeUserbindnamekeywordLikevalue% keyword %/select * from user_info where user_name like #{keywordLike}/select这样 SQL 更清晰也更方便维护。4. 常见用法二统一不同参数路径在实际开发中同一个 SQL 条件可能会在多个查询中复用但参数结构不一样。比如一个查询方法直接传入regionList另一个查询方法传入包装对象query.regionList如果公共 SQL 片段中直接写死foreachcollectionregionList那么它只能适用于第一种情况。这时可以使用bind把不同路径的参数统一成一个名字。示例bindnameregionFiltervalueregionList/includerefidRegion_Filter/或者bindnameregionFiltervaluequery.regionList/includerefidRegion_Filter/公共 SQL 片段中统一使用sqlidRegion_FilteriftestregionFilter ! null and regionFilter.size() 0and region_code inforeachcollectionregionFilteritemitemopen(close)separator,#{item}/foreach/if/sql这样公共 SQL 不需要关心原始参数来自哪里只需要使用统一后的regionFilter。5. 常见用法三复用公共 SQL 片段如果多个查询都需要同样的过滤条件不抽公共片段时可能会重复写很多次iftestregionList ! null and regionList.size() 0and region_code inforeachcollectionregionListitemitemopen(close)separator,#{item}/foreach/if可以把它抽成公共 SQLsqlidRegion_FilteriftestregionFilter ! null and regionFilter.size() 0and region_code inforeachcollectionregionFilteritemitemopen(close)separator,#{item}/foreach/if/sql使用时先通过bind绑定统一变量名bindnameregionFiltervalueregionList/includerefidRegion_Filter/如果参数在对象里bindnameregionFiltervaluequery.regionList/includerefidRegion_Filter/6. 示例单值参数特殊处理假设有一个查询条件areaCode。业务规则如下如果areaCode同时包含A和B则查询A、B两类数据否则按照原值精确查询可以写成sqlidArea_Single_FilteriftestareaFilter ! null and areaFilter ! choosewhentestareaFilter.indexOf(A)gt; 0 and areaFilter.indexOf(B)gt; 0and area_code in (A, B)/whenotherwiseand area_code #{areaFilter}/otherwise/choose/if/sql调用时bindnameareaFiltervalueareaCode/includerefidArea_Single_Filter/这里bind的作用是把原始参数areaCode统一命名为areaFilter方便公共 SQL 片段复用。7. 示例集合参数特殊处理假设有一个集合参数areaCodeList。业务规则如下遍历集合中的每一个值如果某个值同时包含A和B则追加A,B否则追加原值可以写成sqlidArea_List_FilteriftestareaListFilter ! null and areaListFilter.size() 0and area_code inforeachcollectionareaListFilteritemitemopen(close)separator,choosewhentestitem ! null and item.indexOf(A)gt; 0 and item.indexOf(B)gt; 0A,B/whenotherwise#{item}/otherwise/choose/foreach/if/sql调用时bindnameareaListFiltervalueareaCodeList/includerefidArea_List_Filter/如果参数在包装对象中bindnameareaListFiltervaluequery.areaCodeList/includerefidArea_List_Filter/8.bind和${}的区别有些场景也可以使用include的property配合${}实现。例如includerefidArea_List_FilterpropertynamelistParamvaluequery.areaCodeList//include公共片段中iftest${listParam} ! null and ${listParam}.size() 0这种方式虽然可以实现动态替换但不推荐滥用。主要原因${}是字符串替换可读性较差IDE 容易标红使用不当可能带来 SQL 注入风险后期维护成本更高相比之下bind更适合统一参数名的场景。推荐bindnameareaListFiltervaluequery.areaCodeList/includerefidArea_List_Filter/不推荐iftest${listParam} ! null and ${listParam}.size() 09.#{}和${}的区别9.1#{}#{}是参数占位符。示例where user_id #{userId}MyBatis 会使用预编译参数类似whereuser_id?优点安全防 SQL 注入推荐用于普通参数值9.2${}${}是字符串替换。示例order by ${orderColumn}MyBatis 会直接把内容拼进 SQL。如果传入create_time最终 SQL 可能是orderbycreate_time但如果使用不当可能造成 SQL 注入风险。所以一般建议能用#{}就不要用${}。10.bind的注意事项10.1bind只是定义临时变量bind本身不会直接生成 SQL。例如bindnamekeywordLikevalue% keyword %/这行不会出现在最终 SQL 中。它只是定义变量后续需要通过下面方式使用#{keywordLike}或者在动态判断中使用iftestkeywordLike ! null10.2bind的value是 OGNL 表达式MyBatis 动态 SQL 中的test和value通常使用 OGNL 表达式。例如bindnamekeywordLikevalue% keyword %/iftestkeyword ! null and keyword ! whentesttype ACTIVE这些都是 OGNL 表达式。10.3 XML 中要转义特殊符号在 XML 中部分符号需要转义。原符号XML 写法lt;gt;lt;gt;amp;例如下面写法不推荐whentestindex 0应写成whentestindexgt; 010.4 公共 SQL 中的变量名要先绑定如果公共 SQL 片段中使用areaListFilter那么调用公共 SQL 前必须先绑定bindnameareaListFiltervalueareaCodeList/否则运行时可能找不到对应变量。11. 推荐写法推荐bindnameareaListFiltervaluequery.areaCodeList/includerefidArea_List_Filter/公共片段sqlidArea_List_FilteriftestareaListFilter ! null and areaListFilter.size() 0and area_code inforeachcollectionareaListFilteritemitemopen(close)separator,#{item}/foreach/if/sql不推荐iftest${listParam} ! null and ${listParam}.size() 0原因是${}属于字符串替换安全性和可维护性都不如bind。12. 总结bind是 MyBatis 动态 SQL 中非常实用的标签。它的核心作用是把一个表达式或参数路径绑定成一个临时变量方便后续 SQL 复用。适合使用bind的场景包括模糊查询参数拼接复杂表达式提前计算不同参数路径统一命名公共 SQL 片段复用减少重复动态 SQL在实际开发中如果多个查询需要复用同一段动态 SQL但入参路径不同可以使用bind先统一变量名再引用公共 SQL 片段。这样代码更清晰也更容易维护。