背景

近日,某个系统的测试环境mybatis总是报Invalid bound statement(not found)异常,导致tomcat容器无法启动。异常信息如下:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.xxx.management.dao.IssueDao.countByCid
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.$Proxy126.countByCid(Unknown Source)
at com.xxx.management.service.issue.IssueService.countByCid(IssueService.java:81)
at com.xxx.management.service.issue.IssueService$$FastClassBySpringCGLIB$$be57e1e9.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)

QA同学开始以为是develop分支有代码改动导致,切到master分支重新部署,还是出现一样的问题,可是诡异的是相同的代码其实在2天前已经上线了,线上表现一切正常。于是开发同学(我)开始介入排查问题。

注意:两个环境的云主机配置,OS版本,JDK版本,tomcat版本完全一致。

问题定位

首先,我尝试本机复现,发现无论是develop分支还是master分支在本机都不会出现异常,甚至直接去测试环境scp war包到本地启动都无法复现。这就比较抓瞎了,于是只能根据错误日志开始假设排除。

假设1:mybatis interface 和 xml 映射代码错误

异常信息非常直观,就是mybatis查找不到 com.xxx.management.dao.IssueDao.countByCid 这个statement了,第一反应检查mybaits dao相关代码。

由于系统使用的是interface + xml的映射方式,所以先检查 xml 文件的namespace,没问题;再检查 <select> 的 id,也没问题,parameterType、resultType等属性均无问题。

google 上相关问题的解决方案还有人说改动一下maven的打包配置,但确认过xml和dao都已打进jar包,因此可以确定不是 dao interface 和 xml 的映射代码有问题。

假设2:mybatis mapper scan 配置错误

系统使用mybatis-spring集成,mybaits MapperScan 的配置如下:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.xxx.common.dao,com.xxx.management.dao"/>
</bean>

出问题的dao interface是 com.xxx.management.dao.IssueDao,根据异常的堆栈信息远程debug打断点(系统使用的mybatis版本为3.4.6)后发现抛异常的代码如下:

  • MapperMethod 227行:

  • MapperMethod 251行:

原因就在于 mapperInterface.equals(declaringClass),断点观察后发现两者的值都是com.xxx.management.dao.IssueDao,因此可以断定MapperScan的配置没有问题,basePackage正常生效了。

假设3:mybatis mapper locations 配置错误

mybatis-spring还有一项mapper locations的配置,系统中的配置如下:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations">
<array>
<value>classpath:/mybatis/common/*.xml</value>
<value>classpath:/mybatis/*.xml</value>
</array>
</property>
</bean>

先说明一下为啥mapperLocations有两个,且两者都是/mybatis目录下,是因为它们分别在两个不同的jar包里。classpath:/mybatis/common/*.xml 在依赖的一个二方库lib-commons.jar中,classpath:/mybatis/*.xml 则在自身工程的manage-moudle-dao.jar中。

从配置文件可知,mapperLocations被赋值给了SqlSessionFactoryBean.mapperLocations:

查找一下this.mapperLocations的调用,发现被 SqlSessionFactoryBean.buildSqlSessionFactory()方法调用,调用代码段如下:

代码作用简单来说就是解析 xml mapper,并且解析成功后会打印一条DEBUG日志,于是去查 tomcat 启动日志,发现并没有Parsed mapper file: '[mybatis/IssueDao.xml]' 的日志,于是远程debug在遍历this.mapperLocations处,发现并未加载到 /myabtis目录下的任何文件,仅加载到了/mybatis/common目录下的文件,而 this.mapperLoactions并无其他write调用,因此可以断定问题出在mapperLoactions的spring属性赋值上。

问题分析

从上文SqlSessionFactoryBean的代码截图可以看到,mapperLocations实际类型是Resource[],这个是被spring在解析BeanDefinition时做的转换,通过的是ResourcePatternResolver接口,具体到本例上是PathMatchingResourcePatternResolver。

PathMatchingResourcePatternResolver.getResources方法的代码如下:



注:常量CLASSPATH_ALL_URL_PREFIX = "classpath*:"。

从代码中可知,classpath*: 会查找classpath下的所有符合条件的resource,而 classpath:则只会查找第一个符合条件的resource, 本例中使用的classpath:。

因此spring应当是先加载了lib-commons.jar中的/mybatis/common/*.xml,然后根据第二个location去加载/mybatis/*.xml,因为第一个location的配置中也有/mybatis/,所以根据classpath:只查找第一个符合条件的原则,直接在已命中过的lib-commons.jar中搜索/mybatis/*.xml,而没有再去搜索其他jar包。

问题解决

知道原因在哪就很好办了,有两种办法:

  1. 把classpath:改成classpath*:
  2. 改变xml文件路径,让两个location不会有重叠路径

经过实际验证,两种方法均有效。

扩展

问题到上面其实已经解决了,但是还有一个问题遗留着:同样的代码,甚至是同一个war包,在不同的机器上的表现为啥完全不一致?

因为已知机器的系统版本、JDK版本、tomcat版本都是完全相同的,所以我猜测这个是否和JVM的jar包加载顺序有关呢?

google一番,参考 https://my.oschina.net/ericquan8/blog/1523496 这篇文章,可知 linux 系统下 jvm 其实是优先加载 inode 值更小的 jar ,去各环境实测发现确实如此。

Mybatis-Spring扫描路径有重叠导致Invalid bound statement(not found)问题的更多相关文章

  1. mybatis替换成mybatisplus后报错mybatisplus Invalid bound statement (not found):

    项目原来是mybatis,之后由于生成代码不方便,觉得替换成mybatisplus,引入mybatisplus后,启动项目报错mybatisplus Invalid bound statement ( ...

  2. mybatis Invalid bound statement (not found)错误解决办法

    由于新版的IntelliJ IDEA不再编译source folder下的xml文件,而我们平时使用mybatis时,习惯于将*Mapper.xml文件放在与dao层.service层平级的src目录 ...

  3. Spring扫面路径配置不全导致异常 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 的原因

    运行Junit测试类 package cn.bgodata.x.zero.service; import org.junit.Test; import org.junit.runner.RunWith ...

  4. 【spring boot Mybatis】报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.newhope.interview.dao.UserMapper.add

    报错如下: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.newhope.i ...

  5. Spring boot结合mybatis开发的报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    错误:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found),经过排查确定是没有找到xml的原因 ...

  6. Invalid bound statement (not found)--spring boot集成mybatis

    问题: {"timestamp":"2019-07-02T10:21:32.379+0000","status":500,"err ...

  7. Mybatis 异常记录(1): Invalid bound statement (not found)

    错误信息: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.pingan.cr ...

  8. mybatis报错:Invalid bound statement (not found)

    mybatis报错:Invalid bound statement (not found)的原因很多,但是正如报错提示一样,找不到xml中的sql语句,报错的情况分为三种: 第一种:语法错误 Java ...

  9. Springboot项目下mybatis报错:Invalid bound statement (not found)

    mybatis报错:Invalid bound statement (not found)的原因很多,但是正如报错提示一样,找不到xml中的sql语句,报错的情况分为三种: 第一种:语法错误 Java ...

随机推荐

  1. TigerGraph入门

    测试机器配置 1G内存,1个核,CentOS Linux release 7.4.1708 (Core)的云主机,一块50G HDD的云主机. 1. 安装 下载了目前最新的开发者版本,下载链接:htt ...

  2. Shiro authentication for Apache Zeppelin

    Overview Apache Shiro is a powerful and easy-to-use Java security framework that performs authentica ...

  3. SQL SERVER 活动监视-sys.dm_exec_requests

    sys.dm_exec_requests (Transact-SQL)应用: 针对 SQL Server 内正在执行的每个请求返回一行.sys.dm_exec_connections.sys.dm_e ...

  4. C#3.0新增功能07 查询表达式

    连载目录    [已更新最新开发文章,点击查看详细] 查询是什么及其作用是什么 查询是一组指令,描述要从给定数据源(或源)检索的数据以及返回的数据应具有的形状和组织. 查询与它生成的结果不同. 通常情 ...

  5. 基于ng-zorro的ASP.NET ZERO前端实现之代码生成器

    上一篇介绍了集成ng-zorro的过程,本篇我们来看下如何用abp官方的生成器来生成前端代码. Abp官方提供了一个强大的代码生成器 ASP.NET Zero Power Tools,它的Visual ...

  6. MySql的数据库优化到底优化啥了都(3)

    嘟嘟在上两个文章里面简单粗糙的讲了讲关于MySql存储引擎的一些特性以及选择.个人感觉如果面试官给我机会的话,至少能说个10分钟了吧.只可惜有时候生活就是这样:骨感的皮包骨头了还在那美呢.牢骚两句,北 ...

  7. Unity AR Foundation 和 CoreML: 实现手部的检测和追踪

    0x00 前言 Unity的AR Foundation通过上层抽象,对ARKit和ARCore这些底层接口进行了封装,从而实现了AR项目的跨平台开发能力. 而苹果的CoreML是一个可以用来将机器学习 ...

  8. cesium 学习(六) 坐标转换

    cesium 学习(六) 坐标转换 一.前言 在场景中,不管是二维还好还是三维也罢,只要涉及到空间概念都会提到坐标,坐标是让我们理解位置的一个非常有效的东西.有了坐标,我们能很快的确定位置相关关系,但 ...

  9. 《PHP从入门到精通(第3版)》目录

    一.基础知识 1.初识PHP 2.PHP环境搭建和开发工具 3.PHP语言基础 4.流程控制语句 5.字符串操作 6.正则表达式 7.PHP数组 8.PHP与Web页面交互 9.PHP与JavaScr ...

  10. 自动装配、JavaConfig、XML 三种方案之间,怎么导入和混合配置?

    在 Spring 中,这些配置方案都不是互斥的.完全可以将 JavaConfig 的组件扫描和自动装配/或 XML 配置混合在一起. Q:如何在 JavaConfig 中引用 XML 配置? Q:怎么 ...