使用MyBatis框架时Mapper传参是否需要使用@Param注解

在使用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 annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
        if (isSpecialParameter(paramTypes[paramIndex])) {
            // skip special parameters
            continue;
        }
        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 #71
            name = 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编译器在编译代码时是否保留了参数的实际名称,如果未保留参数实际名称则返回null
  private 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注解原理


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,在下面评论区告诉我^_^^_^