能配,但需严格遵循配置规范:数据源须用不同前缀(如master/slave)隔离,事务管理器一对一绑定并标注@Primary,SqlSessionFactory必须明确指定对应数据源,AbstractRoutingDataSource需清理ThreadLocal上下文。

能配,但别直接抄网上“两段@Configuration+@Primary”的模板——90%的线上事故都出在事务管理器没对齐、MyBatis的SqlSessionFactory绑定错源、或者AbstractRoutingDataSource里线程上下文没清理干净。
application.yml 里数据源配置必须带明确前缀
Spring Boot 不会自动识别“两个 spring.datasource”,必须用不同前缀隔离,否则后加载的会覆盖前一个:
-
spring.datasource.master和spring.datasource.slave是最安全的命名方式,避免和 Spring Boot 自动配置的默认spring.datasource冲突 - 每个数据源必须单独指定
driver-class-name,MySQL 8+ 必须用com.mysql.cj.jdbc.Driver,旧版驱动在高并发下可能抛SQLException: Connection is closed -
url中的serverTimezone=Asia/Shanghai和useUnicode=true&characterEncoding=utf-8缺一不可,否则中文入库乱码或时间字段偏移
事务管理器必须一对一绑定数据源
一个 DataSource 就得配一个 PlatformTransactionManager,否则 @Transactional 注解会静默失效:
将 Spring Boot 2.7 项目升级到 Spring Boot 3.5 的实战流程,覆盖版本基线、依赖坐标替换、Jakarta 迁移、配置兼容、异步上下文传递改造与验证门禁。用于企业多模块 Maven 项目升级与排障。
- 主库事务管理器必须标注
@Primary,否则 Spring 无法决定默认用哪个 - 从库操作如果也加了
@Transactional,必须显式指定管理器名:@Transactional("slaveTransactionManager") - 千万别把两个
DataSourceTransactionManager都设为@Primary,Spring 启动直接报NoUniqueBeanDefinitionException
MyBatis 的 SqlSessionFactory 和 MapperScan 要严格隔离
MyBatis 不会自动按数据源分发 SQL,必须手动切断绑定链:
- 每个
SqlSessionFactoryBean 必须明确 setDataSource() 到对应数据源实例,不能共用 -
@MapperScan的basePackages和sqlSessionFactoryRef必须成对出现:主库 mapper 扫描路径只能引用主库的SqlSessionFactory,反之亦然 - 如果用了 MyBatis-Plus,
MybatisPlusAutoConfiguration会抢注默认SqlSessionFactory,必须在spring.autoconfigure.exclude里排除它,否则启动报BeanDefinitionOverrideException
动态路由必须处理好线程上下文泄漏
用 AbstractRoutingDataSource 实现读写分离时,getTargetDataSource() 返回的只是数据源引用,真正切换靠的是 ThreadLocal 存的 key —— 这个 key 不清理,一次请求残留会影响后续请求:
- 务必在过滤器或拦截器里调用
DynamicDataSourceContextHolder.clear()(或你自定义的清空方法),且放在finally块中 - 不要在异步线程(如
@Async方法)里触发数据源切换,ThreadLocal不传递,会 fallback 到默认源 - 事务方法内先查后写,比如
@Transactional里调slaveMapper.selectById()再调masterMapper.update(),必须确保查操作发生在写之前,否则从库可能读不到刚写入的主库数据(最终一致性窗口)
最常被跳过的点是事务管理器和 SqlSessionFactory 的绑定关系——配错一个,整个数据源就形同虚设,SQL 全走默认源,连日志都看不出异常。

















