MyBatis 接口方法参数不加 @Param,照样能正常取值的真相
很多人都有这种体验:有些 Mapper 接口方法不写 @Param 也能正常在 XML 中用 #{xxx} 取到值,而有些情况不加就会报错 “Parameter ‘xxx’ not found”。这不是 bug,而是 MyBatis 参数命名机制的几种不同处理规则 导致的。
核心一句话:
当 Mapper 接口方法只有一个参数时,可以不写 @Param,MyBatis 会直接使用该参数本身作为值(或用参数名取值,如果编译时保留了参数名)。
当有多个参数时,不加 @Param 就会用默认命名(param1、param2、0、1…),这时候如果 SQL 里写了 #{userId} 就会找不到。
下面详细说明几种常见情况和底层原理。
1. 只有一个参数时:可以不加 @Param(最常见“照样取值”场景)
// Mapper 接口
User findById(Long id);
// XML
<select id="findById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
为什么能取到?
MyBatis 在处理单参数时,会直接把这个参数对象作为值,不再包装成 Map。
所以 #{id}、#{value}、#{anything} 其实都能取到(只要不是 null),MyBatis 会忽略 #{ } 里的名字,直接用参数本身。
更现代的写法(推荐):
WHERE id = #{id}
-- 或
WHERE id = #{value} <!-- 也能取到 -->
注意:即使你写了 #{abc} 也能取值,但为了可读性,还是建议写成 #{id}。
2. 多个参数时:不加 @Param 也能用,但名字是 param1 / param2 / 0 / 1…
List<User> findByNameAndAge(String name, Integer age);
XML 可以这样写(不加 @Param 也行):
<select id="findByNameAndAge">
SELECT * FROM user
WHERE name = #{param1} AND age = #{param2}
-- 或者
WHERE name = #{0} AND age = #{1}
</select>
MyBatis 底层逻辑:
- 多参数时,MyBatis 会把所有参数包装成一个 Map
- 没有 @Param 时,默认 key 是:
param1、param2、param3…(从 1 开始) - 同时也支持索引方式:
0、1、2…(从 0 开始) - 所以上面两种写法都能运行
但强烈不推荐这种写法,因为:
- 可读性极差
- 参数顺序一改就全错
- 维护成本高
3. 加了 @Param 后,名字就是注解指定的值(最推荐做法)
List<User> findByNameAndAge(@Param("userName") String name, @Param("userAge") Integer age);
WHERE name = #{userName} AND age = #{userAge}
清晰、可读、顺序无关 → 生产环境标准写法。
4. 特殊情况:即使只有一个参数,也建议加 @Param 的场景
虽然单参数可以不加,但以下情况强烈建议加:
- 参数是基本类型或 String,想在 SQL 中用
#{xxx}明确名字 - 动态 SQL 中用作变量(
<if test="xxx != null">等) - 将来可能会加参数(提前养成好习惯)
- 团队规范统一(避免有人写 param1,有人写 0)
5. 为什么有些项目不加 @Param 也能用参数名?(高级情况)
MyBatis 3.4.0+ 开始支持:
- 如果项目编译时加了
-parameters参数(Java 8+ 支持) - 编译器会把方法参数名保留到字节码中(.class 文件)
- MyBatis 就能通过反射拿到真正的参数名(如
id、name)
// 编译时加 -parameters
User findById(Long id); // 就可以在 XML 中写 #{id}
但依赖编译配置,不够可靠,大部分项目不这么做,不建议依赖这个特性。
总结:加不加 @Param 的推荐规则(2025-2026 最佳实践)
| 参数数量 | 推荐做法 | XML 中怎么写 | 说明 |
|---|---|---|---|
| 0 个 | 无需 @Param | — | — |
| 1 个 | 建议加 @Param | #{id}、#{userId} | 可读性好,未来扩展安全 |
| 2 个及以上 | 必须加 @Param | #{userName}、#{age} | 否则只能用 param1 / 0 |
| 特殊情况 | 动态 SQL、将来扩展 | 加 @Param | 预防性编程 |
一句话口诀:
一个参数可以不加,多个参数必须加;为了可读和维护,建议一律都加。
你项目里现在是单参数不加、还是统一都加 @Param?遇到过因为不加导致的坑吗?可以贴代码,我帮你看下具体场景。