1. 多数据源配置中的"找不到主数据源"异常解析
第一次在Spring Boot项目里集成MyBatis-Plus多数据源时,看到控制台蹦出"dynamic-datasource can not find primary datasource"的红色错误,我整个人都是懵的。这就像你新买了个智能家居中枢,结果它一直报错说找不到主控设备——明明所有配件都装好了啊!
这个异常的本质是动态数据源管理器在启动时找不到默认的master数据源。想象你是个餐厅经理,服务员每次接到顾客点单都需要知道该把单子送到哪个厨房(数据源)。如果顾客没指定特殊厨房(@DS注解),按照规矩应该送到主厨房(master),但突然发现主厨房根本不存在,这可不就乱套了么?
在实际项目中,我遇到过三种典型触发场景:
- 配置文件中完全没定义primary数据源
- 虽然声明了primary: master,但下面根本没配置master数据源的具体参数
- 启用了strict模式却漏配了某些模块的数据源
# 典型错误配置示例 spring: datasource: dynamic: primary: master # 这里声明了默认用master datasource: orderDB: # 但实际只配置了orderDB url: jdbc:mysql://localhost:3306/order username: root password: 1234562. 深度拆解dynamic-datasource运行机制
要真正解决这个问题,得先弄明白MyBatis-Plus多数据源的工作流程。这就像修车不能只看故障灯,得打开发动机盖看看内部构造。
dynamic-datasource的核心工作原理可以分为三个阶段:
2.1 初始化阶段
项目启动时,DataSourceAutoConfiguration会读取yml配置,创建所有定义的数据源对象。这里有个关键点:它会检查primary指定的默认数据源是否存在。就像开学时班主任要确认班长人选,如果名单里根本没有这个人,肯定要出问题。
// 简化的初始化逻辑 public void afterPropertiesSet() { if (!dataSourceMap.containsKey(primary)) { throw new CannotFindDataSourceException("can not find primary datasource"); } }2.2 运行时路由阶段
当执行DAO方法时,系统会按这个优先级确定使用哪个数据源:
- 方法上的@DS注解(最高优先级)
- 类上的@DS注解
- 配置文件中的primary数据源(兜底选择)
2.3 strict模式的影响
这个配置项就像严格的交通警察:
- strict: false时,找不到指定数据源就默默用primary
- strict: true时,直接抛出异常中断执行
spring: datasource: dynamic: strict: true # 建议生产环境开启3. 六种实战解决方案对比
经过多个项目的踩坑实践,我总结出六种解决"找不到主数据源"的方法,各有适用场景。
3.1 基础修复方案
方案A:补全master配置最直接的解决方式,适合新项目:
datasource: dynamic: primary: master datasource: master: # 补全主数据源 url: jdbc:mysql://localhost:3306/core username: root password: 123456 orderDB: url: jdbc:mysql://localhost:3306/order方案B:修改primary指向如果已有其他数据源,可以改指向:
datasource: dynamic: primary: orderDB # 指向已存在的数据源3.2 进阶配置方案
方案C:类级别@DS注解适合模块化清晰的项目:
@DS("orderDB") // 整个类默认使用orderDB @Repository public class OrderDaoImpl implements OrderDao { // 方法可以不加注解 }方案D:启用多主数据源3.3.0版本后支持多主源配置:
datasource: dynamic: primary: master,orderDB # 多个主数据源 strict: false3.3 生产环境推荐方案
方案E:分组数据源对于读写分离等场景特别实用:
datasource: dynamic: primary: master datasource: master_1: master_2: slave_1: slave_2: groups: master: master_1,master_2 # 主库组 slave: slave_1,slave_2 # 从库组方案F:动态解析方案通过自定义解析器实现灵活路由:
public class TenantDataSourceResolver { public static String determineDataSource() { // 根据租户ID等业务参数决定数据源 } }4. 避坑指南与最佳实践
在金融项目里踩过几次坑后,我总结出这些血泪经验:
4.1 配置检查清单
每次新增数据源时,建议对照这个清单检查:
- primary指定的数据源名称是否拼写正确
- 对应数据源的url/username/password是否完整
- 多模块项目是否所有模块都配置了默认数据源
- strict模式是否符合当前环境需求
4.2 日志调试技巧
在application.yml增加以下配置,可以清晰看到数据源切换过程:
logging: level: com.baomidou.dynamic.datasource: DEBUG典型调试日志示例:
2023-08-20 14:00:00 DEBUG - Switching to datasource: [orderDB] 2023-08-20 14:00:01 DEBUG - Reverting to datasource: [master]4.3 事务管理要点
多数据源环境下事务要特别注意:
- 避免跨数据源事务(需要分布式事务支持)
- @Transactional和@DS注解的优先级问题
- 建议在Service层统一管理事务边界
@DS("orderDB") @Service public class OrderServiceImpl { @Transactional // 这里的事务会在orderDB上生效 public void createOrder() { // ... } }5. 典型场景故障模拟
为了帮大家更深入理解,我搭建了测试环境重现了几个典型错误场景。
5.1 案例一:未指定primary
配置文件中完全省略primary声明:
# 错误配置 datasource: dynamic: datasource: orderDB: url: jdbc:mysql://localhost:3306/order控制台会立即抛出:
CannotFindDataSourceException: dynamic-datasource can not find primary datasource5.2 案例二:strict模式误用
启用了strict但漏配数据源:
@DS("inventoryDB") // 但配置里只有master和orderDB public void updateStock() { // ... }会看到:
CannotFindDataSourceException: Could not find datasource: inventoryDB5.3 案例三:名称拼写错误
经典的"大小写敏感"问题:
datasource: dynamic: primary: MASTER # 实际配置是master datasource: master: url: ...6. 性能优化建议
当数据源数量超过5个时,这些优化手段能显著提升性能:
6.1 连接池配置
建议为每个数据源单独配置连接池参数:
datasource: dynamic: datasource: master: url: ... hikari: maximum-pool-size: 20 connection-timeout: 300006.2 延迟初始化
对于非核心数据源可以启用延迟加载:
spring: datasource: dynamic: lazy: true # 延迟初始化6.3 监控集成
配合Micrometer实现数据源监控:
@Bean public DataSourceMetrics dataSourceMetrics(DataSource dataSource) { return new DataSourceMetrics(dataSource, "myapp"); }