昨天在新建Springboot启动后,发现执行相关的SQL报错 org.apache.ibatis.binding.BindingException: Invalid bound statement,具体报错信息如下:

 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hqtc.live.admin.common.dao.UserDao.list
at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:227)
at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:49)
at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:65)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:58)
at com.sun.proxy.$Proxy89.list(Unknown Source)
at com.hqtc.live.admin.common.service.impl.UserServiceImpl.list(UserServiceImpl.java:47)
at com.hqtc.live.admin.common.service.impl.UserServiceImpl$$FastClassBySpringCGLIB$$eed42660.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.hqtc.live.admin.common.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$137fcb94.list(<generated>)
at com.hqtc.live.admin.common.shiro.UserRealm.doGetAuthenticationInfo(UserRealm.java:46)
at org.apache.shiro.realm.AuthenticatingRealm.getAuthenticationInfo(AuthenticatingRealm.java:568)
at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:180)
at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267)
at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
at org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106)
at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:270)
at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:256)
at org.apache.shiro.web.filter.authc.AuthenticatingFilter.executeLogin(AuthenticatingFilter.java:53)
at org.apache.shiro.web.filter.authc.FormAuthenticationFilter.onAccessDenied(FormAuthenticationFilter.java:154)
at org.apache.shiro.web.filter.AccessControlFilter.onAccessDenied(AccessControlFilter.java:133)
at org.apache.shiro.web.filter.AccessControlFilter.onPreHandle(AccessControlFilter.java:162)
at org.apache.shiro.web.filter.PathMatchingFilter.isFilterChainContinued(PathMatchingFilter.java:203)
at org.apache.shiro.web.filter.PathMatchingFilter.preHandle(PathMatchingFilter.java:178)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:131)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

我在网上找了好多相关的blog,好多说是

  1:Mapper.xml中的namespace不对应和mapper接口不对应

  2:Mapper.xml中的方法(即id)和mapper接口中的方法名字不同或对应的方法不存在

  3:返回类型不匹配(即没有正确配置ResultMap或者ResultType)

  4:可能xml文件有缓存或者修改后没保存

  5:可能没有配置MapperScan导致dao方法没有被扫描注入

  6:配置文件中mybatis.mapper-locations或mybatis.typeAliasesPackage配置不正确

我逐条对比测试,发现不是上述的这些问题,那么问题出在哪里呢?

通过报错信息的第二行,我找到了MapperMethod.SqlCommand方法,具体代码如下:

     public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}

通过分析我们得出进入了ms==null的逻辑,因此我们分析MapperMethod.resolveMappedStatement这个方法,看为什么这个sql对应的MappedStatement为什么为空,分析其代码如下

     private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}

其中configuration.hasStatement(statementId)会判断有没有这个sql对应的方法。分析发现,org.apache.ibatis.session.Configuration方法中的属性 mappedStatements 为空。这就不难解释为什么报找不到方法的错误了。

那么为什么mappedStatements为空呢,我怀疑是mybatis.mapper-locations属性没有被正确加载解析,导致无法正确的找到每个接口方法对应的sql,那么接下来分析mybatis.mapper-locations有没有被加载。Mybatis相关的配置信息被加载后都存在MybatisProperties类中,通过调试查看这个类的属性发现,mybatis.mapper-locations已经被加载了。那么接下来看有没有被解析。我们找到了MybatisAutoConfiguration.sqlSessionFactory方法,具体代码如下:

   @Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
} return factory.getObject();
}

在最后你是不是看到了

 if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}

按道理来说,这个方法是被@Bean注解修饰的,应该会被Spring自动注入,一单被自动注入,那么相应的会执行这个方法。但是通过调试发现,这个方法没有被执行,难怪mybatis.mapper-locations属性没有被正确解析。我突然想到,我们当时在做mysql主从的时候,在相关的配置类中自定义过 SqlSessionFactory 的实现和注入,有可能是这个问题。那么我尝试将我们自定义的SqlSessionFactory的实现给注释掉,重启后发现问题解决了。我们看下 MybatisAutoConfiguration 中关于 sqlSessionFactory 的注入代码部分如下:

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

我们看到了 @ConditionalOnMissingBean 这个注解,它的意思是如果BeanFactory中存在了 SqlSessionFactory,就不再注入当前的 SqlSessionFactory。这也就是我们自定义SqlSessionFactory的注入方法后,就不在执行这个注入代码的原因。

最后发现其实问题很简单,就是我们自定义的SqlSessionFactory 的注入覆盖了mybatis内部的注入方法,导致mybatis.mapper-locations没有被解析生效。

最后总结:

1:重视报错信息,通过报错信息我们往往能分析到问题产生的直接原因。

2:找到问题间的关联。就像上述问题,如果你熟悉mybatis的源码,你可能会很快的找到根本原因。如果你不熟悉源码,可以通过断点调试等方法,找到相关类和方法间的引用关系从而找到问题的关键。

3:还是多看看mybatis的源码吧。

关于Mapper.xml生效的问题的更多相关文章

  1. MyBatis Mapper.xml文件中 $和#的区别

    MyBatis Mapper.xml文件中 $和#的区别   网上有很多,总之,简略的写一下,作为备忘.例子中假设参数名为 paramName,类型为 VARCHAR . 1.优先使用#{paramN ...

  2. Mybatis学习错误之:重复加载mapper.xml

    学习mybatis的时候,突然遇到测试出错.测试mapper代理失败,现在钻研少了,不喜欢看未知的错误了,立即改正.错误打印说mapper.xml已经注册,仔细查看SQLMapConfig.xml发现 ...

  3. mybatis公用代码抽取到单独的mapper.xml文件

    同任何的代码库一样,在mapper中,通常也会有一些公共的sql代码段会被很多业务mapper.xml引用到,比如最常用的可能是分页和数据权限过滤了,尤其是在oracle中的分页语法.为了减少骨架性代 ...

  4. Mybatis学习--Mapper.xml映射文件

    简介 Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心. 映射文件中有很多属性,常用的就是parameterType(输入类型 ...

  5. nested exception is java.lang.RuntimeException: Error parsing Mapper XML. Cause: java.lang.IllegalArgumentException: Result Maps collection already contains value for

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'daoSupport': ...

  6. mybatis mapper.xml 配置文件问题(有的错误xml是不报的) 导致服务无法启动 。

    转载自 开源编程 一舟mybatsi xml编译报错,tomcat启动一直循环,导致内存溢出,启动失败 mapper.xml怎么知道有没有编译错误,哪个位置有错误 这应该是mybatis的一个bug, ...

  7. mybatis之mapper.xml分析

    select: id:方法名,在同一个mapper.xml中,要保持唯一 parameterType:指定输入的参数类型,不是必须的,如果不指定,mybatis会自动识别(推荐指定). resultT ...

  8. spring mybatis 整合问题Error parsing Mapper XML. Cause: java.lang.NullPointerException

    14:30:40,872 DEBUG SqlSessionFactoryBean:431 - Parsed configuration file: 'class path resource [myba ...

  9. Mapper XML Files详解

    扫扫关注"茶爸爸"微信公众号 坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活. Mapper XML Files The true power of MyBatis ...

随机推荐

  1. AirFlow常用命令

    airflow常用命令如下所示: airflow test dag_id task_id execution_date 测试task 示例: airflow test example_hello_wo ...

  2. JSP官方文档(JavaServer Pages Specification)下载

    找了好久,终于找到官网的下载地址了.这样,就可以用一手的文档资料了! 下载地址:http://download.oracle.com/otndocs/jcp/jsp-2_3-mrel2-spec/ s ...

  3. 装系统 ------ 使用微PE 做系统盘

    1.什么是PE系统 pe系统是一种装系统的系统,也就是预装系统的系统,它是一种系统预装环境和工具. 可以放在U盘或光盘里随身携带,可以用来给电脑装系统 2.常见的制作pe 系统的工具 大白菜,U启动, ...

  4. 42 (OC)* 字典实现原理--哈希原理

    一.NSDictionary使用原理 1.NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的,hash函数设计的好坏影响着数据的查找访问效率. - (void ...

  5. Vue学习之vue中的计算属性和侦听器

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. jqGrid 日期格式化,只显示日期,去掉小时分

    {name:'operateTime',index:'operateTime', formatter:"date", formatoptions: {newformat:'Y-m- ...

  7. elasticsearch的分布式基础概念(1)

    Elasticsearch对复杂分布式机制的透明隐藏特性 Elasticsearch是一套分布式的系统,分布式是为了应对大数据量 隐藏了复杂的分布式机制 分片机制(随随便便就将一些document插入 ...

  8. C++ 生成随机数 srand()和rand()

    1. rand() rand(产生随机数)表头文件: #include<stdlib.h>定义函数 :int rand(void) 函数说明 :因为rand() 的内部实现是用线性同余法做 ...

  9. Spring 梳理-传递模型数据到视图

    传递model,返回string,不指定返回key package com.jt; import com.myOrg.DeptUserDomain; @Controller @RequestMappi ...

  10. Spring MVC-从零开始-文件上传(未完待续)

    Spring MVC-从零开始-文件上传(未完待续)