Skip to content

MyBatis 动态 SQL 参数异常 ​

问题现象 ​

出现 Parameter not found、空 IN ()、SQL 语法错误,或者筛选条件为空时意外查出全表。

适用环境 ​

MyBatis 3、Java Mapper 与 XML;示例使用明确 @Param。

本文是可复用的排障示例,不代表某次个人生产事故。命令中的名称、账号和数据均为示例,执行前请替换为已确认的目标。

复现或诊断步骤 ​

  1. 对照 Mapper 方法参数名、@Param 和 XML 的 collection/test 表达式。
  2. 在开发环境查看实际 SQL 与绑定参数,敏感值脱敏。
  3. 分别输入 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 * 也可能掩盖字段映射遗漏。

官方参考链接 ​

相关分类:Java 后端 · 全部笔记