困扰我一整天的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"
})

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

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


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

相关推荐
ShineWinsu8 分钟前
对于JavaScript:JavaScript基础语法的解析
开发语言·前端·javascript·面试·web·基础语法·循环
救救孩子把1 小时前
AI面试八股文-13_12-AI 框架与运维面试题
运维·人工智能·面试
救救孩子把2 小时前
AI面试八股文-14_14-MCP-Skill-Function Calling
人工智能·安全·面试·职场和发展·推理·mcp·functioncall
帷幕落秋3 小时前
进程服务与文件描述符
linux·运维·面试
帷幕落秋3 小时前
信号、Ctrl 组合键与进程状态
linux·运维·面试
预知同行4 小时前
Agent 开发正在降温,但「可治理」的火才刚点起来
面试
毕业设计7038 小时前
(免费领源码)SSM董家湾线上点餐系统04682- java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
java·redis·mysql·tomcat·maven·mybatis·ssm
乌暮9 小时前
从 `println` 到生产级日志:Spring Boot 日志体系完全指南
java·开发语言·jvm·spring boot·后端·学习·面试
王中阳Go10 小时前
第一单交付完,我等 3 个月没复购:不是交付差,是这 3 件事没做
人工智能·后端·面试
叫我铁汁儿吧10 小时前
JS作用域
前端·面试