深浅主题
MyBatis 动态 SQL 参数异常
问题现象
出现 Parameter not found、空 IN ()、SQL 语法错误,或者筛选条件为空时意外查出全表。
适用环境
MyBatis 3、Java Mapper 与 XML;示例使用明确 @Param。
本文是可复用的排障示例,不代表某次个人生产事故。命令中的名称、账号和数据均为示例,执行前请替换为已确认的目标。
复现或诊断步骤
- 对照 Mapper 方法参数名、@Param 和 XML 的 collection/test 表达式。
- 在开发环境查看实际 SQL 与绑定参数,敏感值脱敏。
- 分别输入 null、空集合、单元素和多元素;区分字段不存在、类型不匹配与空条件业务语义。
原因分析
Java 编译是否保留参数名可能改变隐式命名行为。foreach 对空集合不能自动决定业务上应查询全部还是返回空。使用字符串替换拼接值还会产生 SQL 注入和类型转换问题。
修复示例
xml
<!-- Java: List<Item> selectByIds(@Param("ids") List<Long> ids); -->
<select id="selectByIds" resultType="example.Item">
SELECT id, name FROM item
<where>
<choose>
<when test="ids != null and ids.size() > 0">
id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</when>
<otherwise>1 = 0</otherwise>
</choose>
</where>
</select>为多个参数显式命名,值使用 #{} 绑定。动态列名或排序无法作为普通值绑定时,必须从服务端固定允许列表选择。查询空集合返回空结果是本例约定;更新和删除更应在服务层拒绝无条件操作。
验证方法
对四类集合输入核对 SQL 与结果数,并测试包含引号的普通文本不会改变 SQL 结构。分页插件和租户插件启用时,还要检查改写后的 SQL。
常见误区
不要只通过关闭参数校验避错。where 标签只解决 AND 拼接,不会保证查询条件符合业务范围;select * 也可能掩盖字段映射遗漏。