看似简单的配置冲突,却让我排查了整整一整天
问题起源
最近在整理一个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注解扫描了两次。
问题根源分析
重复扫描的副作用
- Bean重复注册:同一个Mapper接口会被Spring尝试注册两次
- Bean名称冲突:默认情况下,Mapper接口的Bean名称是接口名的首字母小写
- 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注解即可。
经验总结
- 配置统一原则:相关的配置应该集中管理,避免分散在多处
- 关注启动类:检查问题时要特别注意启动类中的注解配置
- 理解框架机制:了解Spring Boot版本差异带来的行为变化
- 代码审查要点:在代码审查时,要特别注意重复的注解配置
配置建议
对于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"
})
希望我的这次排查经历能够帮助到遇到类似问题的开发者,避免在这个问题上浪费不必要的时间!
技术之路,细节决定成败!
关注我,获取更多实用技术干货和实战经验!