【MyBatis】MyBatis 报错:Parameter ‘xxx‘ not found
MyBatis 报错:Parameter ‘xxx’ not found
错误信息示例:
Parameter ‘testId’ not found. Available parameters are [arg1, arg0, param1, param2]
本文结合实际项目中的一次问题定位与修复过程,系统性分析 MyBatis 中常见的参数名解析问题,解释根因、重现方式、排查路径、正确写法与最佳实践,帮助你在遇到类似错误时高效定位与修复。
一、错误现象与触发场景
我们在新增整编需求时,需要根据 testId
与 ageId
去 data_collection_requirement
表里查询对应的 form_number
。Mapper 方法与 XML 大致如下:
- Mapper 接口(问题版本)
String selectFormNumberByTestAndyage(Long testId, Long ageId);
- Mapper XML(配套 SQL)
<select id="selectFormNumberByTestAndyage" resultType="String">select form_numberfrom data_collection_requirementwhere test_task_id = #{testId}and voyage_id = #{ageId}limit 1
</select>
执行时报错:
- Parameter ‘testId’ not found. Available parameters are [arg1, arg0, param1, param2]
这意味着 MyBatis 在解析参数名称时没有识别到 testId
,而是只看到了默认生成的参数别名 arg0/arg1/param1/param2
。
二、根因分析:MyBatis 如何识别多参数名称
在 Mapper 接口方法有多个参数时,如果未开启 Java 编译参数名保留或未显式标注 @Param
,MyBatis 无法获取源码中的真实参数名,只能分配默认名:
- 第一个参数:
arg0
/param1
- 第二个参数:
arg1
/param2
- 以此类推
因此,XML 中使用 #{testId}
、#{ageId}
时会找不到对应名称,触发上述错误。
常见导致该问题的几种情况:
- 未使用
@Param
指定参数名; - 工程未通过
-parameters
编译选项保留参数名; - Lombok/代理/多层调用导致参数名信息丢失;
- 混用 XML 中的
#{xxx}
与接口未命名的参数。
三、三种可行修复方案(从业务稳定性角度推荐优先级)
方案 A(推荐):使用 @Param 显式命名
在 Mapper 接口方法参数上标注 @Param
,强制为每个参数指定稳定的名称:
import org.apache.ibatis.annotations.Param;String selectFormNumberByTestAndage(@Param("testId") Long testId,@Param("ageId") Long ageId);
XML 不变:
where test_task_id = #{testId}and voyage_id = #{ageId}
优点:明确稳定,不依赖编译器或构建参数,团队协作成本最低。
方案 B:启用编译参数保留(可选辅助)
在 Maven/Gradle 中开启 -parameters
,让 MyBatis 能读取到真实参数名:
- Maven 示例:
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><compilerArgs><arg>-parameters</arg></compilerArgs></configuration>
</plugin>
注意:不同 JDK 或构建链路、代理增强等仍可能导致参数名不可用,因此通常与 @Param
搭配更稳妥。
方案 C:在 XML 中使用默认别名(不推荐)
直接改 XML 使用 #{param1}
、#{param2}
:
where test_id = #{param1}and age_id = #{param2}
缺点:可读性差、易出错,接口参数顺序变动就会引入隐藏问题。
四、实战案例:从报错到修复的完整过程
- 报错:
Parameter 'testId' not found. Available parameters are [arg1, arg0, param1, param2]
- 触发 SQL:
<select id="selectFormNumberByTestAndage" resultType="String">select form_number from data_collection_requirement where test_task_id = #{testId} and voyage_id = #{ageId} limit 1
</select>
- 修复:为 Mapper 接口参数添加
@Param
注解:
String selectFormNumberByTestAndage(@Param("testId") Long testId,@Param("ageId") Long ageId);
- 结果:问题消失,SQL 正常执行,
form_number
成功回填。
五、排查清单(Checklist)
- Mapper 接口是否为多参数?若是,尽量使用
@Param
明确命名。 - XML 中
#{xxx}
与接口参数名是否一一对应?是否存在拼写差错? - 是否开启了
-parameters
?即便开启,也建议使用@Param
保底。 - 是否有 AOP/代理/字节码增强导致参数名丢失?
- 是否混用了注解 SQL 与 XML,参数名是否一致?
六、相关最佳实践
- 多参数场景一律使用
@Param
,命名与数据库字段或业务语义对齐; - 团队代码规范中明确 MyBatis 映射参数的书写规则;
- 评审重点检查:Mapper 方法签名、XML 参数名、SQL 片段的一致性;
- 对公共查询方法进行集成测试,覆盖典型与边界输入;
- 避免在 XML 中使用
(${xxx})
拼接,必要时使用<foreach>
安全展开,防注入; - 保持参数名的幂等稳定,减少因重构改名引入的线上问题。
七、扩展:为什么 Available parameters 里有两套命名?
MyBatis 为了兼容性,会同时提供 argN
与 paramN
两套下标式别名:
arg0/arg1/...
与方法参数顺序一一对应;param1/param2/...
也是按顺序,从 1 开始计数;
当你看见错误里只有这些别名,而没有你的业务名时,几乎可以断定是未识别到真实参数名导致。
八、结论
- 该错误本质是多参数未显式命名导致的参数映射失败;
- 最稳妥的修复是:为每个参数添加
@Param
注解; - 配合
-parameters
仅作为辅助手段,不应替代@Param
; - 通过统一规范与代码评审,可以从源头规避此类问题反复发生。
九、参考与进一步阅读
- MyBatis 官方文档:
Mapper XML Files
、Mapper Interfaces
- MyBatis Github Issues 中关于参数名与
-parameters
的讨论 - 团队内代码规范与示例库(建议新增多参数映射的标准模板)