Mybatis中dao接口多参数不使用@Param报错

错误描述

apache.ibatis.binding.BindingException: Parameter 'xxx' not found. Available parameters are [argl, arg0, paraml, param2]

问题

  1. 为什么使用@Param执行形参别名不会出现问题?
  2. xxxMapper.xml文件中sql语句的#{}、${}使用的变量名 是如何映射 Dao层接口中的方法参数的?(包括使用@Param和不使用@Param的情况)

分析

1. @Param

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Param {
  String value();
}

其中value就是@Param(‘XXX’)中指定的XXX,也就是Dao层接口方法的形参的别名。

2. 底层代码
Mybatis执行查询时,xxxMapper.xml中的sql语句 是通过 org.apache.ibatis.reflection.ParamNameResolver 类中的 ParamNameResolver 方法 找到Dao层接口方法中的参数的。

  public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // 遍历方法的所有形参,paramIndex为形参在方法参数中的位置
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      // name是Dao层接口中方法的形参名
      String name = null;
      // 遍历Dao层接口方法当前形参的注解
      for (Annotation annotation : paramAnnotations[paramIndex]) {
      	// 如果当前形参使用注解,判断注解是否是@Param类型
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          // 是,就获取@Param指定的形参的别名
          // 这样xml中的sql语句 就能在 Dao层接口方法中根据形参别名找到对应的形参
          name = ((Param) annotation).value();
          break;
        }
      }
      // 如果Dao层接口方法形参没有使用@Param
      if (name == null) {
        // 那么,判断Mybatis是否允许使用形参的参数名作为索引(下面有解释,重点)
        // 也就是是否允许xml文件中sql使用Dao层接口中形参名 查找 对应的形参
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        // 如果不允许使用参数真实名称
        if (name == null) {
          // 就使用当前参数的位置,作为sql语句查找接口方法的 索引
          // use the parameter index as the name ("0", "1", ...)
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

我们当前讨论的前提是:Dao层接口方法多参数形况下不使用 @Param,那么Mybatis就会通过 config.isUseActualParamName() 方法:

public boolean isUseActualParamName() {
	return useActualParamName;
}

config是mybatis的配置对象,里面的配置项目可以影响mybatis的行为,具体配置项目可以从mybatis官方文档查询,这里我们就看一下useActualParamName参数的含义:

设置名描述
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true 或者 false

允许我们使用mapper接口方法的参数名称当作sql语句的参数名称,而且也不需要@Param注解,这个属性默认是开启的,使用这个特性还有以下几个要求:

①采用 Java 8 编译。
②编译时加上-parameters 选项。
③mybatis在3.4.1以上

-parameters 可以在IDEA的 Settings -> Build -> Comliler -> Java Compiler 中设置:
在这里插入图片描述

总结

❗❗❗所以,如果我们在编译时加上 -parameters ,那么就可以使用Dao层接口方法的真实形参名,使得sql语句中的#{}、${}引用的变量名 能够找到 Dao层接口方法中对应的形参。就不会报找不到的错误了。

问题1: 为什么使用@Param执行形参别名不会出现问题?
回答:

// 如果当前形参使用注解,判断注解是否是@Param类型
if (annotation instanceof Param) {
	hasParamAnnotation = true;
    // 是,就获取@Param指定的形参的别名
    // 这样xml中的sql语句 就能在 Dao层接口方法中根据形参别名找到对应的形参
    name = ((Param) annotation).value();
    break;
}

问题2: xxxMapper.xml文件中sql语句的#{}、${}使用的变量名 是如何映射 Dao层接口中的方法参数的?(包括使用@Param和不使用@Param的情况)
回答:
Mybatis执行查询时,xxxMapper.xml中的sql语句 是通过 org.apache.ibatis.reflection.ParamNameResolver 类中的 ParamNameResolver 方法 找到Dao层接口方法中的参数的。

1、是否使用@Param,是就获取@Param中的形参别名
2、否则,判断是否允许使用 Dao层接口方法真实形参名,是就获取对应形参名
3、否则,就是用形参在接口方法中的参数

例子

验证一下-parameters这个选项的作用:

public static void test(int param1, String param2, Object param3){
	// 方法体内容
}
  • 不使用 -parameters
    parameterMethodTest方法的参数 编译后:arg0、arg1、arg2
  • 使用 -parameters
    parameterMethodTest方法的参数 编译后:param1、param2、param3

那对于sql:
select * from test where param1 = #{param1} and param2 = #{param2} and param3 = #{param3}

  • 如果使用了-parameters ,在执行的时候就能通过 param1param2param3 找到接口方法中对应的形参。
  • 如果没有使用-parameters ,sql语句在执行时,通过 param1 = #{param1} and param2 = #{param2} and param3 = #{param3} 寻找方法对应参数,发现方法中的参数是 arg0、arg1、arg2 ,那么就会报 Parameter 'xxx' not found. 错误。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值