在使用MyBatis作为Java项目的ORM框架时,在Mapper接口中传递参数需要通过@Param
注解指定参数名称,这样才能在Mapper接口对应的xml文件中引用到对应名称的参数。如果不在Mapper接口中明确使用@Param
注解时将会报错:找不到指定名称的参数
。
追根溯源,这要从MyBatis获取Mapper接口参数名的实现说起,如下源码:
// ParamNameResolver.java
// 在ParamNameResolver构造函数中获取Mapper接口参数的实现思路如下:
// 1.如果参数使用注解@Param,则取注解值作为参数名;
// 2.如果参数未使用注解@Param,就尝试通过反射方式获取Mapper接口的参数名;
// 3.如果在第2步仍然未获取到参数名,则使用参数索引值(如:0,1,2....)作为参数名;
public ParamNameResolver(Configuration config, Method method) {this.useActualParamName = config.isUseActualParamName();final Class<?>[] paramTypes = method.getParameterTypes();final Annotation[][] paramAnnotations = method.getParameterAnnotations();final SortedMap<Integer, String> map = new TreeMap<>();int paramCount = paramAnnotations.length;// get names from @Param annotationsfor (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {if (isSpecialParameter(paramTypes[paramIndex])) {// skip special parameterscontinue;}String name = null;for (Annotation annotation : paramAnnotations[paramIndex]) {if (annotation instanceof Param) {hasParamAnnotation = true;name = ((Param) annotation).value();break;}}if (name == null) {// @Param was not specified.if (useActualParamName) {name = getActualParamName(method, paramIndex);}if (name == null) {// use the parameter index as the name ("0", "1", ...)// gcode issue #71name = String.valueOf(map.size());}}map.put(paramIndex, name);}names = Collections.unmodifiableSortedMap(map);
}// 通过注解方式获取Mapper参数名
private String getActualParamName(Method method, int paramIndex) {return ParamNameUtil.getParamNames(method).get(paramIndex);
}// ParamNameUtil.java
public class ParamNameUtil {public static List<String> getParamNames(Method method) {return getParameterNames(method);}public static List<String> getParamNames(Constructor<?> constructor) {return getParameterNames(constructor);}// 这里依赖Java编译器在编译代码时是否保留了参数的实际名称,如果未保留参数实际名称则返回nullprivate static List<String> getParameterNames(Executable executable) {return Arrays.stream(executable.getParameters()).map(Parameter::getName).collect(Collectors.toList());}private ParamNameUtil() {super();}
}
综上所述,在MyBatis框架的Mapper接口中是否需要使用注解@Param
明确指定参数名称,依赖Java编译器在编译代码时是否保留了方法参数的实际名称。
那么问题就转变成了:Java编译器在什么时候会保留方法参数的实际名称呢?依赖2个条件:
1.JDK版本必须是8及以上,参见:java8中新增编译参数parameters入门,jdk1.8 开启-parameters参数,编译保留参数名,为反射提供便利。
2.在Maven插件maven-compiler-plugin
中明确设置编译参数,根据插件版本不同,设置编译参数的方式各异,如下:
- 版本3.6.2之前
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.6.0</version><configuration><!-- 添加编译JDK8+支持的编译参数-parameters,保留方法参数名称 --><compilerArgs>-parameters</compilerArgs></configuration>
</plugin>
- 版本3.6.2及之后
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.6.0</version><configuration><!-- 添加编译JDK8+支持的编译参数-parameters,保留方法参数名称 --><parameters>true</parameters></configuration>
</plugin>
当然,在IDEA中运行代码时,如果没有明确配置maven-compiler-plugin
插件的编译参数,还可以设置项目模块的编译参数,如下:
【参考】
MyBatis多参数传递之@Param究竟加还是不加?
关于Mybatis的@Param注解
Mybatis省略@Param注解原理