0575『绍兴』e「网」:一个排查了大半天儿的<问题>,《差点又让》 MyBatis 背锅

admin 5个月前 (05-18) 科技 34 0

我是鹞子, 民众号[「《古时的鹞子》」,‘一个不只有手艺的’手艺 民众号[,一个 在[程序圈混迹多年,主业 Java,《另外》 Python、React 也玩儿的 6 『的斜杠开发者』。
Spring Cloud 系列文章已经完成,可以到 我的github {上}查看系列完整内容。也可以 在[ 民众号[内回复「pdf」{获取我}精心制作的 pdf 版完整教程。

(『写』代码)多年,我一直有个习惯,《只》要是要做的功效 【模块】[《不是很庞大》,一样平常都是{上}来狂『写』(一通代码),等功效做好‘《〖了〗》’,再启动<服务>测试,那里有问题再改(实话说,单元测试『写』的也不多)。《而不是『写』》完一个接口或方式就测试一下,最长的纪录应该是连着『写』4、5天代码,然后一把测试通过,那感受,爽到可以多吃一碗饭。

代码路{上}的滑铁卢

然而,【就 在[前两天】,我感受遭遇到‘《〖了〗》’代码人《生的滑铁卢》,着实遇到过不只一次‘《〖了〗》’,每次滑完铁,再爬起来逐步就忘‘《〖了〗》’。【这次】,我把它『写』下来,这样就不会忘‘《〖了〗》’。

‘事情是’这样的,前两天要对项目加个功效。项目 ORM 接纳的是 MyBatis,由于增加‘《〖了〗》’数据库表,「以是要对应的天生」 DAO 层和 MyBatis 「映射文件」(mapper.xml)。“由于对之前营业不”是熟(悉),我只是先把各个实体类啊、营业类啊、「映射文件」啊、“枚举类”啊等等都建出来,然后『写』‘《〖了〗》’两个简朴接口【准备调试一下】,【『于是』我点‘《〖了〗》’启动按钮】,没问题,〖没有一点错误〗,项目正常启动‘《〖了〗》’,看{上}去是那么的完善。

(我组织)‘《〖了〗》’一个请求,计划测一下刚刚『写』好的接口,当请求发送出去之后,一个熟(悉)的异常泛起 在[‘《〖了〗》’ IDEA 「控制台『中』」,invalid bound statement (not found),用过 MyBatis 的同砚生怕没有不认识这个异常的,它的意思就是我们挪用 DAO (方式的时刻), 在[ mapper.xml 文件『中』没有找到对应的 statement,‘或者说是没有找到你’界说的 SQL 查询语句块。

《泛起这》个异常可能是下面的这几个【‘缘故原’由】:

  1. xml 《文件的》 namespace 和对应的接口名不一‘致’
  2. 接口类『中』的方式和 xml 《文件『中』的》 statement id 对应不{上}
  3. xml 文件『中』有『中』文注释
  4. 随意 在[ xml 文件『中』加一个空格或者空行然后保留,可能能解决问题

【若是你】是用工具自动天生 xml 「还好」,若是是手动建立的,那很可能由于疏忽泛起这个问题,(好比我们从另一个文)件“复制”过来,《遗》忘改 namespace ‘《〖了〗》’,或者接口方式名和 statement id 差‘《〖了〗》’一个字母或者字母顺序不一‘致’。(这个异常是很令人头疼)的,就好比相差一个字母这种情形,很难被“发现”,以是最好照样『写』好接口方式名,然后复制到 xml 『中』。

我虽然有段时间没有碰 MyBatis ‘《〖了〗》’,作为一个老司机,我碰着这个问题着实一点也不慌,由于虽然是工具自动天生的 xml 文件,【然则】我确实又加‘《〖了〗》’几个 statement 块儿,而且 id 也是手敲的,<而且>报错简直实也是我(手动加{上}的),以是, 我预测应[该是名字没对{上},敲错字母或者顺序不一‘致’,『于是』我进去排查‘《〖了〗》’一下,【然则】没“发现”什么问题,为‘《〖了〗》’保险起见,我又到接口『中』把方式名字复制到 xml 『中』‘《〖了〗》’,然后确定 namespace 没问题,〖没有『中』〗文注释,而且又 在[ xml 『中』加‘《〖了〗》’个空行(虽然从来没用这个方式解决过问题),然后重新启动项目,【然则】,异常照样没有消逝。

实时跳出来,不要陷 在[内里

这就有点新鲜‘《〖了〗》’,又重新检查‘《〖了〗》’一遍,没错,都正常,看不出问题所 在[。当确定没有问题的时刻,就要跳出来‘《〖了〗》’,得从其他偏向或者更高条理考虑一下‘《〖了〗》’,否则很可能就陷 在[内里‘《〖了〗》’。划重点,这是多次教训总结出来{的纪律}。(我可以确定当)前挪用的这个接口方式和 statement 「都完全」没有问题,【那很有可能】是其余问题, 会不会是[这个 xml 文件没有被编译打包进去,(『于是』)我进到 target 目录查探一番,有的,【而且查看内容】,确定是没有问题的。

有时刻问题很新鲜,{可能和} IDE 有关,『于是』我用 mvn clean 下令清理‘《〖了〗》’一下,然后重新运行,【然则】,问题依旧 在[。

接下来,我又试‘《〖了〗》’删除这个 xml ,然后新建‘《〖了〗》’一个,【然则】,问题依旧。

‘再往外跳’,你不是这个方式有问题吗, 那我再新建一个方式,就『写』一条最简朴的 SQL,方式名也起的简朴一点,看看会不会有问题,效果,“发现”新大陆‘《〖了〗》’,这个新建的方式也报这个错误。那就有‘《〖了〗》’新的排查偏向‘《〖了〗》’,我再试试其余接口『中』的方式呢,效果,{这}个包名下的几个方式,〖全都有这个错误〗,而其他包名下的方式则没有问题,由于差别功效的 xml 文件放 在[差别的包下,也就是差别的路径下。

那我就知道‘《〖了〗》’,是 xml 文件扫描出问题‘《〖了〗》’,肯定是 MyBatis ‘设置的’ mapperLocations 有问题‘《〖了〗》’,〖有可能〗是被我或者其他同事不小心多敲‘《〖了〗》’个字母之类的。『于是』打开设置文件看‘《〖了〗》’一下,

mybatis:
  mapperLocations: com/xxx/aaa/mapper/*.xml,com/xxx/aaa/bbb/mapper/*.xml,com/xxx/aaa/ccc/mapper/*.xml

MyBatis 设置 mapperLocations 设置‘《〖了〗》’三个包路径,也就是从这三个包『中』寻找 *.xml<去剖析>,【然则】经由检查“发现”,并没有问题,设置文件没有 git 提交纪录,而且‘设置的’包路径也是(准确无误的),其他两个包都扫描正常,就是 com/xxx/aaa/ccc/mapper/*.xml这个包有问题。『于是』我又试‘《〖了〗》’如下几个方式:

  1. 把这个有问题的包路径放到第一个,〖无效〗。
  2. 把其他两个注释,只留这个有问题的,〖无效〗。
  3. 岂非是 MyBatis 读取‘《〖了〗》’其他地方的设置?『于是』我把这个设置注释「掉」,效果都出问题‘《〖了〗》’,说明就是读的这个设置。

<源码大>法好

此时,已经由去很长时间‘《〖了〗》’,问题变的越来越诡异,【然则】事出必有因,肯定是某些地方泛起‘《〖了〗》’问题。着实找不出项目自己的问题‘《〖了〗》’,没设施,我只能怀疑是 MyBatis 有问题‘《〖了〗》’,也许真的是触发‘《〖了〗》’ MyBatis 『自己的隐』藏 bug。

不到万不得已是不会用‘这种方式’的,(那就是调试) MyBatis 源码。想来,MyBatis 源码我照样对照熟(悉)的。那咱们就再见一会吧。

mybatis-spring-boot-starter <只有三个> Java 文件,其『中』 MybatisAutoConfiguration是要害营业类。

而我们知道 MyBatis 『中』 SqlSessionFactory 是异常焦点的工具,以是我们就把断点加 在[ sqlSessionFactory(DataSource dataSource)这个方式{上}。

‘若是’是第一次调试开源框架源码,往往不能一下子找准位置,着实没有关系,把断点打 在[任何一个位置都可以,大不‘《〖了〗》’就逐步跟两遍嘛,自己读源码、调试【的历】程就是个漫长【的历】程。

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
  	// (省略)...
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    return factory.getObject();
}

以{上}代码我只保留‘《〖了〗》’本《次问题相关的》代码,那就是剖析 mapperLocations 【的历】程,也就是{上}面代码『中』this.properties.resolveMapperLocations()这个方式。

public Resource[] resolveMapperLocations() {
    ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    List<Resource> resources = new ArrayList<Resource>();
    if (this.mapperLocations != null) {
      for (String mapperLocation : this.mapperLocations) {
        try {
          Resource[] mappers = resourceResolver.getResources(mapperLocation);
          resources.addAll(Arrays.asList(mappers));
        } catch (IOException e) {
          // ignore
        }
      }
    }
    return resources.toArray(new Resource[resources.size()]);
}

当我继续跟踪代码的时刻,“发现” MyBatis 确实已经识别到‘《〖了〗》’设置《文件『中』的》那三个包路径,this.mapperLocations就是那三个包路径的数组聚集。

接着往下跟, 在[方式 resourceResolver.getResources(mapperLocation)『中』对每一个路径举行剖析,“发现”前两个包都正常返回‘《〖了〗》’Resource[],也就是对应的 xml 文件资源,而【最后】一个返回简直实空数组,问题【‘缘故原’由】已经很近‘《〖了〗》’。

接着再次[启动调试,“当剖析最”后一个包路径是,『进入』resourceResolver.getResources(mapperLocation)方式内部,看看内里都干‘《〖了〗》’什么,【最后】“发现” 在[挪用以下代『码』之后,返回的 rootDirURL (是一个绝对路径),也就是 xml 所 在[的物理路径。

URL rootDirURL = rootDirResource.getURL();

“这时”,终于“发现”问题所 在[‘《〖了〗》’,「这个绝」对路径竟然不是 xml 所 在[的路径,而是《另外》一个子 【模块】[下的路径,经由对比“发现”,<原来>,子 【模块】[『中』被新建‘《〖了〗》’一个名称一样的文件夹,造成存 在[两个完全一样的包路径,而以{上}代码返回‘《〖了〗》’另一个包的绝对路径。『于是』,联系同事,问清晰这个包被建立的【‘缘故原’由】,“发现”是最近新加的【然则】已经废弃无用的,『于是』删「掉」解决‘《〖了〗》’问题。

正常项目开发『中』应该可以规避这种问题,{ 【模块】[与 【模块】[不}应该泛起相同包名,应该遵照如下命名:

【模块】[A:com.kite.moduleA

【模块】[B: com.kite.moduleB

这样从根本{上}解决问题,以防泛起不必要的贫苦。

【最后】

MyBatis 的这个异常确实令人头疼,由于错误【‘缘故原’由】不明显,以此类推,通常 xml 文件造成的问题都不太容易排查,大部分情形都是人为疏忽造成的,而错误一样平常都对照{隐}藏。

当一个问题经由多方验证都没设施被“发现”被解决的时刻,往往就需要换个思绪‘《〖了〗》’,实时跳出来,从其它角度或者更高条「理」重新审阅问题,也许能更快的找到问题【‘缘故原’由】。

在[用开源框架的时刻,『若是泛起问题』,长时间找不到解决设施,那么可以实验调试一下源码,“并没有想象的那么难题”。

壮士且慢,(先给点个赞吧),总是被白嫖,(身体吃)不消!

我是鹞子, 民众号[「《古时的鹞子》」,一个 在[程序圈混迹多年,主业 Java,《另外》 Python、React 『也玩儿的很』 6 『的斜杠开发者』。可以 在[ 民众号[『中』加我密友,(进群里小伙伴交流)学习,很多多少大厂的同砚也 在[群内呦。

,

菲律宾申慱手机登录网址

欢迎『进入』菲律宾申慱手机登录网址!Sunbet 申博提供申博“开户”(sunbet“开户”)、SunbetAPP『下载』、Sunbet客户端『下载』、Sunbet代理合作等业务。

新2备用网址声明:该文看法仅代表作者自己,与本平台无关。转载请注明:0575『绍兴』e「网」:一个排查了大半天儿的<问题>,《差点又让》 MyBatis 背锅

网友评论

  • (*)

最新评论

站点信息

  • 文章总数:714
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1197
  • 评论总数:336
  • 浏览总数:11353