Spring Boot多数据源配置实战:Druid+MyBatisPlus整合指南

📅 2026/7/22 7:31:47 👁️ 阅读次数 📝 编程学习
Spring Boot多数据源配置实战:Druid+MyBatisPlus整合指南

1. 项目概述

在微服务架构盛行的当下,Spring Boot项目经常需要同时连接多个数据库实例。最近在重构一个老旧的会员系统时,就遇到了需要同时操作MySQL用户库和TiDB交易库的场景。经过多次踩坑,终于实现了基于Druid+MyBatisPlus的稳定多数据源方案。

这个方案最大的特点在于:

  • 完全兼容Spring Boot自动配置特性
  • 支持Druid连接池的各项监控指标
  • 保留MyBatisPlus的所有便捷操作
  • 事务管理清晰可控

特别提醒:多数据源配置中最容易犯的错误就是混淆Mapper接口和XML文件的路径配置,这会导致经典的"Invalid bound statement"异常。

2. 核心配置解析

2.1 依赖管理关键点

在pom.xml中需要特别注意这些依赖项:

<properties> <spring-boot.version>2.7.12</spring-boot.version> <druid.version>1.2.16</druid.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version> </properties> <dependencies> <!-- 核心starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <!-- MyBatisPlus必须单独指定版本 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency> </dependencies>

这里有个重要细节:MyBatisPlus默认使用HikariCP,需要通过exclusions强制移除,否则会与Druid产生冲突。

2.2 配置文件陷阱

application.yml的配置需要特别注意格式:

spring: datasource: master: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/master?useSSL=false username: root password: 123456 druid: initial-size: 5 max-active: 20 validation-query: SELECT 1 slave: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/slave?useSSL=false username: root password: 123456 druid: initial-size: 3 max-active: 15

致命陷阱:第一个数据源用url属性,后续数据源必须用jdbc-url,否则启动时会报"jdbcUrl is required"错误。这是Spring Boot自动配置机制的特殊要求。

3. 数据源配置实战

3.1 主数据源配置

@Configuration @MapperScan(basePackages = "com.example.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { @Bean @Primary @ConfigurationProperties("spring.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary public SqlSessionFactory masterSqlSessionFactory( @Qualifier("masterDataSource") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/master/*.xml")); // 关键:注入MyBatisPlus配置 MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); sessionFactory.setConfiguration(configuration); sessionFactory.setPlugins(new MybatisPlusInterceptor()); return sessionFactory.getObject(); } // 其他必要bean... }

3.2 从数据源配置

@Configuration @MapperScan(basePackages = "com.example.mapper.slave", sqlSessionFactoryRef = "slaveSqlSessionFactory") public class SlaveDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public SqlSessionFactory slaveSqlSessionFactory( @Qualifier("slaveDataSource") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/slave/*.xml")); // 复用主数据源的MyBatisPlus配置 MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setMapUnderscoreToCamelCase(true); sessionFactory.setConfiguration(configuration); sessionFactory.setPlugins(new MybatisPlusInterceptor()); return sessionFactory.getObject(); } // 其他必要bean... }

4. 事务管理方案

4.1 单数据源事务

直接在Service方法上使用@Transactional即可:

@Service @RequiredArgsConstructor public class OrderService { private final OrderMapper orderMapper; // 主库Mapper @Transactional public void createOrder(Order order) { orderMapper.insert(order); } }

4.2 跨数据源事务

需要引入JTA实现(如Atomikos):

@Bean public JtaTransactionManager transactionManager() { return new JtaTransactionManager(); } // 使用时指定事务管理器 @Transactional(transactionManager = "transactionManager") public void crossDatabaseOperation() { masterMapper.insert(...); slaveMapper.update(...); }

性能警告:分布式事务会显著降低系统吞吐量,建议通过最终一致性方案替代。

5. 监控配置技巧

Druid的监控面板配置:

@Bean public ServletRegistrationBean<StatViewServlet> druidServlet() { ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); reg.addInitParameter("loginUsername", "admin"); reg.addInitParameter("loginPassword", "admin"); return reg; } @Bean public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() { FilterRegistrationBean<WebStatFilter> reg = new FilterRegistrationBean<>(); reg.setFilter(new WebStatFilter()); reg.addUrlPatterns("/*"); reg.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*"); return reg; }

访问http://localhost:8080/druid即可查看SQL监控、URI监控等数据。

6. 常见问题排查

6.1 Mapper绑定失败

错误现象:

BindingException: Invalid bound statement (not found)

解决方案:

  1. 检查@MapperScan的basePackages是否包含对应Mapper接口
  2. 检查setMapperLocations路径是否包含XML文件
  3. 确认XML中的namespace与Mapper接口全限定名一致

6.2 事务不生效

可能原因:

  1. 方法不是public
  2. 自调用(this.method())
  3. 异常被catch未抛出
  4. 数据库引擎不支持事务(如MyISAM)

6.3 连接泄漏

Druid监控面板查看:

  • activeCount持续增长
  • 检查代码中是否忘记关闭Connection/Statement/ResultSet

7. 性能优化建议

  1. 连接池参数调优:

    druid: min-idle: 5 max-active: 50 max-wait: 3000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000
  2. MyBatisPlus二级缓存:

    @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new CachingInnerInterceptor()); return interceptor; }
  3. SQL监控慢查询阈值:

    druid: filter: stat: log-slow-sql: true slow-sql-millis: 1000

8. 扩展思考

对于更复杂的多租户场景,可以考虑:

  1. 动态数据源路由(AbstractRoutingDataSource)
  2. Schema级别隔离
  3. MyBatisPlus租户插件

在K8s环境中部署时,建议:

  1. 使用ConfigMap管理数据源配置
  2. 为每个Pod设置合理的连接池大小
  3. 实现健康检查接口监控连接状态