Spring Boot项目里MyBatis-Plus Dynamic-Datasource主数据源失效?别慌,5分钟搞定配置
2026/6/8 5:04:59 网站建设 项目流程

Spring Boot多数据源配置:彻底解决Dynamic-Datasource主数据源失效问题

刚接触Spring Boot多数据源配置的开发者,经常会遇到一个令人头疼的问题——明明按照文档配置了数据源,却在启动时抛出CannotFindDataSourceException: dynamic-datasource can not find primary datasource异常。这种情况在项目紧急上线或快速迭代时尤为常见,本文将带你深入理解问题根源,并提供多种实用解决方案。

1. 问题现象与核心原因分析

当你在Spring Boot项目中集成MyBatis-Plus的dynamic-datasource组件时,可能会遇到以下典型错误日志:

com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException: dynamic-datasource can not find primary datasource

问题本质在于动态数据源组件无法找到默认的主数据源。这通常发生在以下两种场景:

  1. 配置文件中没有声明primary指定的数据源名称对应的具体数据源
  2. 代码中既没有类级别的@DS注解,也没有方法级别的@DS注解

dynamic-datasource组件有一个重要设计原则:当没有明确指定数据源时,默认会尝试使用名为master的数据源。如果此时配置中既没有master数据源,也没有通过primary属性指定其他数据源名称,就会抛出上述异常。

2. 关键配置参数解析

理解dynamic-datasource的两个核心配置参数是解决问题的关键:

2.1 primary参数

spring: datasource: dynamic: primary: master # 设置默认数据源名称
  • 作用:指定默认使用的数据源名称
  • 默认值master
  • 行为:当代码中没有使用@DS注解时,会自动使用此处指定的数据源

2.2 strict参数

spring: datasource: dynamic: strict: true # 严格模式开关
  • 作用:控制数据源匹配的严格程度
  • 默认值false
  • true行为:当指定的数据源不存在时直接抛出异常
  • false行为:当指定的数据源不存在时回退到primary指定的默认数据源

3. 五种实用解决方案对比

根据不同的项目需求和场景,可以选择以下任一方案解决问题:

3.1 方案一:添加类级别@DS注解

@DS("lizzDB") // 指定该类默认使用lizzDB数据源 @Repository public class ErmDaoImpl implements ErmDao { // 类实现... }

适用场景

  • 整个类的所有方法都使用同一个数据源
  • 不想修改现有配置文件的场景

优缺点对比

优点缺点
改动最小,只需添加一个注解如果类中方法需要使用不同数据源则不适用
不影响现有配置需要在每个类上添加注解

3.2 方案二:配置primary指定的数据源

spring: datasource: dynamic: primary: lizzDB # 指定默认数据源名称 lizzDB: # 配置对应的数据源 url: jdbc:mysql://localhost:3306/lizz_db username: user password: pass

适用场景

  • 项目中有明确的默认数据源
  • 希望保持配置集中管理的项目

3.3 方案三:添加master数据源配置

spring: datasource: dynamic: primary: master # 使用默认值 master: # 添加master数据源配置 url: jdbc:mysql://localhost:3306/default_db username: user password: pass lizzDB: url: jdbc:mysql://localhost:3306/lizz_db username: user password: pass

适用场景

  • 希望保持与组件默认行为一致的项目
  • 需要向后兼容已有配置的场景

3.4 方案四:关闭strict模式

spring: datasource: dynamic: strict: false # 关闭严格模式

行为变化

  • 当指定的数据源不存在时,不会抛出异常
  • 自动回退到primary指定的默认数据源

适用场景

  • 需要更高容错性的开发环境
  • 数据源动态变化的特殊场景

3.5 方案五:混合配置策略

对于复杂项目,可以采用组合策略:

spring: datasource: dynamic: primary: default_db # 业务主库 strict: true # 生产环境建议开启 default_db: # 主业务库 url: jdbc:mysql://localhost:3306/main_db username: user password: pass report_db: # 报表库 url: jdbc:mysql://localhost:3306/report_db username: user password: pass

同时在代码中合理使用@DS注解:

@Service @DS("default_db") // 类级别默认数据源 public class OrderServiceImpl implements OrderService { @Autowired private ReportDao reportDao; @DS("report_db") // 方法级别覆盖 public Report generateDailyReport() { // 使用报表库生成报告 } }

4. 最佳实践与常见陷阱

在实际项目开发中,我们总结出以下经验:

4.1 配置建议

  1. 生产环境配置

    spring: datasource: dynamic: primary: main_db strict: true # 生产环境建议开启严格模式
  2. 开发环境配置

    spring: datasource: dynamic: strict: false # 开发环境可关闭严格模式提高容错

4.2 常见错误示例

错误1:primary指定了不存在的数据源

spring: datasource: dynamic: primary: non_exist_db # 错误:配置中没有这个数据源

错误2:strict=true但缺少必要的@DS注解

public class UserService { // 没有@DS注解,strict=true时会尝试使用primary数据源 // 如果primary数据源也不存在就会抛异常 }

错误3:YAML格式缩进错误

spring: datasource: dynamic: primary: master # 错误:缩进不正确导致配置不生效

4.3 调试技巧

当遇到数据源问题时,可以:

  1. 开启debug日志:

    logging: level: com.baomidou.dynamic.datasource: debug
  2. 检查数据源初始化日志:

    DynamicDataSourceProvider - Loaded 2 datasources
  3. 验证数据源切换:

    @Test void testDataSourceSwitch() { // 验证默认数据源 String defaultDs = DynamicDataSourceContextHolder.peek(); assertEquals("master", defaultDs); // 验证注解切换 DynamicDataSourceContextHolder.push("slave"); assertEquals("slave", DynamicDataSourceContextHolder.peek()); }

5. 高级配置与原理深入

对于需要更精细控制数据源的项目,可以了解以下高级特性:

5.1 多数据源组支持

dynamic-datasource支持数据源分组,可以将多个物理数据源作为一个逻辑组使用:

spring: datasource: dynamic: primary: group1 # 使用数据源组作为默认 group1: # 数据源组定义 ds1: url: jdbc:mysql://localhost:3306/db1 ds2: url: jdbc:mysql://localhost:3306/db2

5.2 自定义数据源选择策略

通过实现DynamicDataSourceStrategy接口可以自定义数据源选择逻辑:

public class RandomDataSourceStrategy implements DynamicDataSourceStrategy { @Override public String determineDataSource( List<String> dataSourceKeys) { // 随机选择一个数据源 Random random = new Random(); return dataSourceKeys.get( random.nextInt(dataSourceKeys.size())); } }

然后在配置中指定策略:

spring: datasource: dynamic: strategy: com.example.RandomDataSourceStrategy

5.3 事务管理注意事项

在多数据源环境下,需要特别注意事务管理:

  1. 声明式事务

    @Transactional @DS("order_db") public void placeOrder(Order order) { // 跨数据源操作需要特别注意事务传播 }
  2. 编程式事务

    @Autowired private DataSourceTransactionManager transactionManager; public void batchProcess() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { // 业务逻辑 transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } }

在实际项目中,我们遇到过这样一个案例:一个报表生成服务需要从多个业务库查询数据然后写入报表库。最初的设计是在每个DAO方法上添加@DS注解,但随着业务增长,这种方式变得难以维护。最终我们重构为使用AOP根据方法名自动路由数据源:

@Aspect @Component public class DataSourceRoutingAspect { @Before("execution(* com.example..*Repository.*(..))") public void beforeMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); if (methodName.startsWith("find") || methodName.startsWith("get")) { DynamicDataSourceContextHolder.push("read_db"); } else { DynamicDataSourceContextHolder.push("write_db"); } } @After("execution(* com.example..*Repository.*(..))") public void afterMethod() { DynamicDataSourceContextHolder.poll(); } }

这种设计既保持了代码的简洁性,又能灵活地根据操作类型自动选择合适的数据源。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询