报错信息
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);
}