Mybatis-Spring扫描路径有重叠导致Invalid bound statement(not found)问题
背景
近日,某个系统的测试环境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包。
问题解决
知道原因在哪就很好办了,有两种办法:
- 把
classpath:
改成classpath*:
- 改变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)问题的更多相关文章
- mybatis替换成mybatisplus后报错mybatisplus Invalid bound statement (not found):
项目原来是mybatis,之后由于生成代码不方便,觉得替换成mybatisplus,引入mybatisplus后,启动项目报错mybatisplus Invalid bound statement ( ...
- mybatis Invalid bound statement (not found)错误解决办法
由于新版的IntelliJ IDEA不再编译source folder下的xml文件,而我们平时使用mybatis时,习惯于将*Mapper.xml文件放在与dao层.service层平级的src目录 ...
- 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 ...
- 【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 ...
- Spring boot结合mybatis开发的报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
错误:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found),经过排查确定是没有找到xml的原因 ...
- Invalid bound statement (not found)--spring boot集成mybatis
问题: {"timestamp":"2019-07-02T10:21:32.379+0000","status":500,"err ...
- Mybatis 异常记录(1): Invalid bound statement (not found)
错误信息: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.pingan.cr ...
- mybatis报错:Invalid bound statement (not found)
mybatis报错:Invalid bound statement (not found)的原因很多,但是正如报错提示一样,找不到xml中的sql语句,报错的情况分为三种: 第一种:语法错误 Java ...
- Springboot项目下mybatis报错:Invalid bound statement (not found)
mybatis报错:Invalid bound statement (not found)的原因很多,但是正如报错提示一样,找不到xml中的sql语句,报错的情况分为三种: 第一种:语法错误 Java ...
随机推荐
- redis 发布与订阅原理分析
前言:用了redis也有一段时间了,但是发布与订阅的使用频率也不高,趁着这次空闲,深究下redis的发布与订阅模式. 一.订阅频道和信息发布 功能说明:Redis 的 SUBSCRIBE 命令可以让客 ...
- md文档的书写《三》
markdown语法 官网 这是标题 "#加空格" 是标题,通常可以设置六级标题. 内容下 空格是换行 列表 无序列表:使用" - + * "任何一种加空格都可 ...
- 转载——Asp.Net MVC+EF+三层架构的完整搭建过程
转载http://www.cnblogs.com/zzqvq/p/5816091.html Asp.Net MVC+EF+三层架构的完整搭建过程 架构图: 使用的数据库: 一张公司的员工信息表,测试数 ...
- 201803-1跳一跳 CCF (C语言)
问题描述 近来,跳一跳这款小游戏风靡全国,受到不少玩家的喜爱. 简化后的跳一跳规则如下:玩家每次从当前方块跳到下一个方块,如果没有跳到下一个方块上则游戏结束. 如果跳到了方块上,但没有跳到方块的中心则 ...
- JSTL和EL简介
EL Expression Language,表达式语言,通过操作存在于PageContext等的数据,实现JSP的编写更加简单,单纯使用EL不用引入jar包,只要容器支持即可. EL的隐含对象 EL ...
- GitLab与Git的结合
作为一名刚入职的大数据初级开发工程师,来到公司后发现代码是部署在GItLab上,之前一直认为代码可以放在码云.github上面,然后就很迷惑就对GitLab进行了了解,将git 和gitlab结合起来 ...
- python Django编写接口并用Jmeter测试
一.环境准备 python3.6.7 Pycharm 二.创建项目 我这里是在Django项目中新建了个APP,目录结构如下图所示: 那么怎么在已有的Django项目中新建APP并进行配置呢: 2.1 ...
- java往文本文件中写入信息并修改
题目要求: 1.可以往一个文本文档中写入员工信息:name,id和详情 2.可以更改name package FanCQ.Xue.practice; import java.io.*;import j ...
- 【Spring】org.springframework.web.context.ContextLoaderListen 报错
详细信息如下: org.apache.catalina.core.StandardContext.listenerStart Error configuring application listene ...
- python 处理json数据
python 处理 json数据 以下是登录账号后获取的json数据,headers中注意加入cookie值 需要处理的数据如下: 全部代码如下 #!/usr/bin/env python # -*- ...