项目使用了mybatis,版本为mybatis-3.2.2.jar + mybatis-spring-1.2.0.jar。今天发现项目运行中出现Invalid bound statement (not found)异常。
mybatis在启动的时候会扫描mapper,把mapper类中的方法及对应的statement语句注册到Configuration对象中。既然出现了Invalid bound statement (not found)异常,说明在运行过程中没有找到对应mapper中的方法。我们使用的是注解方式,看了对应mapper的方法,注解都有了,没问题,关联了下源码,debug的时候发现,对应mapper中有部分方法是注册了的,而报错的方法没有被注册,怀疑是代码没编译,重新编译过后,问题还在,猜测可能是mybatis在注册的时候出错了,导致后续方法没有被注册。看了下源码,mybatis在Configuration类的getMapper方法中寻找statement,那肯定会有个地方在初始化的时候注册那些statement,在Configuration类中找到相似方法public <T> void addMapper(Class<T> type),追踪进去,这个方法调用了org.apache.ibatis.binding.MapperRegistry类的addMapper方法,代码如下:
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
追踪进入org.apache.ibatis.builder.annotation.MapperAnnotationBuilder类的parse方法
public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); assistant.setCurrentNamespace(type.getName()); parseCache(); parseCacheRef(); Method[] methods = type.getMethods(); for (Method method : methods) { try { parseStatement(method); } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } parsePendingMethods(); }
可以看到在这边获取了Mapper接口的所有方法,解析方法上的注解,继续追踪,进入parseStatement方法
void parseStatement(Method method) { Class<?> parameterTypeClass = getParameterType(method); LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver); if (sqlSource != null) { Options options = method.getAnnotation(Options.class); final String mappedStatementId = type.getName() + "." + method.getName(); Integer fetchSize = null; Integer timeout = null; StatementType statementType = StatementType.PREPARED; ResultSetType resultSetType = ResultSetType.FORWARD_ONLY; SqlCommandType sqlCommandType = getSqlCommandType(method); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = !isSelect; boolean useCache = isSelect; KeyGenerator keyGenerator; String keyProperty = "id"; String keyColumn = null; if (SqlCommandType.INSERT.equals(sqlCommandType)) { // first check for SelectKey annotation - that overrides everything else SelectKey selectKey = method.getAnnotation(SelectKey.class); if (selectKey != null) { keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver); keyProperty = selectKey.keyProperty(); } else { if (options == null) { keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); } else { keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); keyProperty = options.keyProperty(); keyColumn = options.keyColumn(); } } } else { keyGenerator = new NoKeyGenerator(); } if (options != null) { flushCache = options.flushCache(); useCache = options.useCache(); fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348 timeout = options.timeout() > -1 ? options.timeout() : null; statementType = options.statementType(); resultSetType = options.resultSetType(); } String resultMapId = null; ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class); if (resultMapAnnotation != null) { resultMapId = resultMapAnnotation.value(); } else if (isSelect) { resultMapId = parseResultMap(method); } assistant.addMappedStatement( mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, null, // ParameterMapID parameterTypeClass, resultMapId, // ResultMapID getReturnType(method), resultSetType, flushCache, useCache, false, // TODO issue #577 keyGenerator, keyProperty, keyColumn, null, languageDriver); } }
方法第三行,获取sqlSource,debug到这边的时候有出错(不是报 Invalid bound statement (not found)的犯法),去看了下报错的方法,原来报错的方法使用的是@UpdateProvider注解,但是属性type对应的类中没有method指定的方法,所以在执行代码
getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
时报了,但是项目启动的时候没错误日志,正常启动成功了,再看了下代码
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) { try { Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method); Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method); if (sqlAnnotationType != null) { if (sqlProviderAnnotationType != null) { throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName()); } Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType); final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation); return buildSqlSourceFromStrings(strings, parameterType, languageDriver); } else if (sqlProviderAnnotationType != null) { Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType); return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation); } return null; } catch (Exception e) { throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e); } }
getSqlSourceFromAnnotations方法中会抛出BuilderException,而在之前的parse方法中捕获的是IncompleteElementException
for (Method method : methods) { try { parseStatement(method); } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } }
IncompleteElementException是BuilderException的子类,所以这边并不会被捕获,会继续往上抛,导致循环退出,mapper中的后续方法跳过注册,因此在代码执行的时候没有找到对应的statement。
唉,真是坑爹啊。
相关推荐
在使用Mybatis-Plus时,有时开发者可能会遇到一个常见的错误——`Invalid bound statement (not found)`。这个错误意味着Mybatis-Plus无法找到对应的SQL映射语句,这通常是由于配置问题或者依赖缺失所引起的。本文将...
在使用MybatisPlus时,有时会遇到一个常见的错误——`Invalid bound statement (not found)`,这个错误通常发生在尝试调用BaseMapper中自动生成的方法时。本文将深入探讨这个问题的成因以及解决方法。 首先,`...
在使用MyBatis框架进行开发时,可能会遇到一个常见的错误:`BindingException: Invalid bound statement (not found)`。这个错误通常意味着MyBatis无法找到你在Mapper接口中声明的方法与XML映射文件中对应的SQL语句...
在MyBatis框架中,映射器(Mapper)是核心组件之一,它负责定义SQL语句和结果映射,使得数据库操作与业务逻辑解耦。本文将详细介绍如何在MyBatis配置中引入映射器,包括三种常见方法:文件路径引入、包名引入和类...
- "mybatis错误:Invalid bound statement (not found) -SELECT分页.url":这可能是一个常见问题,可能是由于Mapper接口与XML文件中的SQL语句未匹配导致,需要检查相关配置。 - "3使用MySQL的jdbc驱动关于时区引发的...
在使用Mybatis框架时,你可能会遇到`org.apache.ibatis.exceptions.PersistenceException`这样的异常。这个异常通常表示在执行数据库查询操作时遇到了问题。本篇将详细分析这个问题并提供解决方法。 ### 问题概述 ...
在上述问题中,报错信息是`org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)`,这通常意味着Mybatis-Plus无法找到对应的Mapper方法。让我们分析一下可能的原因和解决方案: 1. **...
如果遇到错误`org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.offcn.mapper.UserMapper.getUserList`,通常是因为映射文件的位置不正确或未包含在构建过程中。你可以尝试...