困扰我一整天的MyBatis"Invalid bound statement"问题,原来是因为这个不起眼的注解冲突!

看似简单的配置冲突,却让我排查了整整一整天

问题起源

最近在整理一个SpringBoot项目的架构时,遇到了一个典型的MyBatis问题:

java 复制代码
Invalid bound statement (not found): com.xx.system.server.dao.SysRoleMapper.pagelist

这个错误对于MyBatis使用者来说再熟悉不过了。无非就是五种常见原因:

五种常规排查方案

第一种:检查namespace是否一致 确认mapper.xml中的namespace和实际的mapper接口完全一致。

第二种:检查方法名是否匹配 核对mapper接口中的方法名和mapper.xml中的id标签,都是pagelist,完全匹配。

第三种:检查文件是否被正确构建 清理maven,重新编译,确认target目录下确实存在对应的mapper.xml文件。

第四种:检查资源文件配置 确认mybatis-plus的mapper-locations配置正确:

yaml 复制代码
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml

第五种:检查Spring配置 仔细检查了所有相关配置,没有发现明显问题。

然而,当我试完这几种解决方案后 依旧没有解决!

意外的发现

在近乎绝望的时候,我无意中查看了项目的启动类BootstrapApplication

java 复制代码
@SpringBootApplication
@MapperScan("com.xx.system.server.dao")
public class BootstrapApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootstrapApplication.class, args);
    }
}

然后又查看了MybatisConfig配置类:

java 复制代码
@Configuration
@MapperScan("com.xx.system.server.dao")
public class MybatisConfig {
    // MyBatis-Plus 相关配置
}

问题就出在这里! 相同的包路径被@MapperScan注解扫描了两次。

问题根源分析

重复扫描的副作用

  1. Bean重复注册:同一个Mapper接口会被Spring尝试注册两次
  2. Bean名称冲突:默认情况下,Mapper接口的Bean名称是接口名的首字母小写
  3. Spring Boot 2.1+的严格机制:Spring Boot 2.1之后默认不允许覆盖Bean定义

为什么会导致"Invalid bound statement"?

在重复扫描的情况下,虽然应用可能正常启动(取决于Spring配置),但MyBatis在构建Mapper代理时可能会出现混乱,导致部分Mapper方法无法正确绑定到对应的SQL语句。

解决方案

保持配置的统一性 ,只在一个地方使用@MapperScan

方案一:只在启动类中配置(推荐)

java 复制代码
@SpringBootApplication
@MapperScan("com.xx.system.server.dao")
public class BootstrapApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootstrapApplication.class, args);
    }
}

方案二:只在配置类中配置

java 复制代码
@Configuration
@MapperScan("com.xx.system.server.dao")
public class MybatisConfig {
    // MyBatis-Plus 相关配置
}

移除另一处的@MapperScan注解即可。

经验总结

  1. 配置统一原则:相关的配置应该集中管理,避免分散在多处
  2. 关注启动类:检查问题时要特别注意启动类中的注解配置
  3. 理解框架机制:了解Spring Boot版本差异带来的行为变化
  4. 代码审查要点:在代码审查时,要特别注意重复的注解配置

配置建议

对于MyBatis扫描配置,我个人建议:

yaml 复制代码
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  type-aliases-package: com.xx.system.server.dao.entity
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

并在启动类中统一配置Mapper扫描:

java 复制代码
@MapperScan({
    "com.xx.system.server.dao"
})

希望我的这次排查经历能够帮助到遇到类似问题的开发者,避免在这个问题上浪费不必要的时间!

技术之路,细节决定成败!


关注我,获取更多实用技术干货和实战经验!

相关推荐
许彰午3 小时前
我手写了一个 Java 内存数据库(二):B+ 树的插入与分裂
java·开发语言·面试
weisian1517 小时前
基础篇--概念原理-2-参数是什么?——从原理到实战,一篇讲透
面试·职场和发展·模型参数·7b和70b·参数=规则,不是原始数据
AI人工智能+电脑小能手9 小时前
【大白话说Java面试题】【Java基础篇】第26题:Java的抽象类和接口有哪些区别
java·开发语言·面试
逻辑驱动的ken11 小时前
Java高频面试考点场景题20
java·开发语言·深度学习·面试·职场和发展
Wect11 小时前
深度剖析浏览器跨域问题
前端·面试·浏览器
刀法如飞13 小时前
Java数组去重的20种实现方式——指导AI解决不同问题的思路
java·算法·面试
ayqy贾杰13 小时前
Cursor SDK发布!开发者可直接搬走其内核
前端·vue.js·面试
JAVA面经实录9171 天前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
小程故事多_801 天前
[大模型面试系列] 多轮对话 Agent 设计实战(含窗口优化 + 工具调用精髓)
人工智能·面试·职场和发展
misL NITL1 天前
idea、mybatis报错Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required
tomcat·intellij-idea·mybatis