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

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

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


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

相关推荐
せいしゅん青春之我4 小时前
【JavaEE进阶】JVM-面试中的高频考点1
java·网络·jvm·笔记·面试·java-ee
Cyan_RA95 小时前
操作系统面试题 — Linux中如何查看某个端口有没有被占用?
linux·后端·面试
飞哥的AI笔记6 小时前
Instant模式和Thinking模式的技术差异是什么?分别适用什么场景?
面试·ai编程
T___T6 小时前
深入浅出:JavaScript 字符串反转的 6 种解法与面试技巧
javascript·面试
9号达人6 小时前
@NotBlank 不生效报错 No validator could be found:Hibernate Validator 版本匹配指北
后端·面试·程序员
踏浪无痕6 小时前
6张表、14步业务逻辑,Mall订单事务凭什么比你的3步事务还稳?
spring boot·spring·面试
therese_1008616 小时前
面试试试试试试题-答
面试
90后小陈老师18 小时前
用户管理系统 05 实现后端注册功能 | Java新手实战 | 最小架构 | 期末实训 | Java+SpringBoot+Vue3
java·开发语言·spring boot·后端·spring·maven·mybatis
前端布鲁伊21 小时前
再来聊聊,Vue3 项目中 Pinia 的替代方案
前端·面试