看源码解决:org.apache.ibatis.binding.BindingException: Parameter '0' not found

报错信息

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter "0" not found. Available parameters are [arg1, arg0, param1, param2]
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
	at com.sun.proxy.$Proxy98.selectList(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)
	at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
	at com.sun.proxy.$Proxy144.getProcessNodeList(Unknown Source)

原因分析

1、Mybatis的Mapper类中,参数前面没有添加@Param("xxx")作为参数说明; 2、Mybatis版本升级到了3.4.2以后的版本;

解决办法

1、在application.properties(或bootstrap.yml,或配置中心等)中添加如下:

mybatis.configuration.use-actual-param-name=false

2、重启应用

源码分析

  • mapper.java中接口参数定义有使用如下配置:
UserModel findById(Integer id);
  • mapper.xml中sql语句有使用到如下
    <select id="findById" resultType="com.gongstring.model.UserModel">
        SELECT user_id userId,user_name userName
        FROM demo_user
        WHERE user_id=#{0}
        ORDER BY time_created DESC 
		LIMIT 1
    </select>

关键点:其中参数获取的时候,使用的{0}

  • org.apache.ibatis.session.Configuration类中
protected boolean useActualParamName = true;

按照源码日志,2016年5月17日的提交记录中,将该参数从原来的false改成了true;猜测是希望在后续版本中减少直接使用{0}、{1}等查询条件,而推荐使用{param1}、{param2}前缀,当然最好是自己写@Param注解声明变量名称。 提交日志见:https://github.com/mybatis/mybatis-3/commit/3f19d04b6ef2a09c452c104d8eb680b37b246b0a#diff-65ade3ef0e889aa04b75e6bb408e3787

  • org.apache.ibatis.reflection.ParamNameResolver

Mybatis服务在启动的时候,会初始化ParamNameResolver名称转换对象,有关于名称变量定义规则:


  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;
    // 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 (config.isUseActualParamName()) {
		  //如果有@Param注解,则优先使用注解名称设置参数值
          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);
  }