Spring Boot多数据源配置实战:Druid+MyBatisPlus整合指南
📅 2026/7/22 7:31:47
👁️ 阅读次数
📝 编程学习
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)解决方案:
- 检查@MapperScan的basePackages是否包含对应Mapper接口
- 检查setMapperLocations路径是否包含XML文件
- 确认XML中的namespace与Mapper接口全限定名一致
6.2 事务不生效
可能原因:
- 方法不是public
- 自调用(this.method())
- 异常被catch未抛出
- 数据库引擎不支持事务(如MyISAM)
6.3 连接泄漏
Druid监控面板查看:
- activeCount持续增长
- 检查代码中是否忘记关闭Connection/Statement/ResultSet
7. 性能优化建议
连接池参数调优:
druid: min-idle: 5 max-active: 50 max-wait: 3000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000MyBatisPlus二级缓存:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new CachingInnerInterceptor()); return interceptor; }SQL监控慢查询阈值:
druid: filter: stat: log-slow-sql: true slow-sql-millis: 1000
8. 扩展思考
对于更复杂的多租户场景,可以考虑:
- 动态数据源路由(AbstractRoutingDataSource)
- Schema级别隔离
- MyBatisPlus租户插件
在K8s环境中部署时,建议:
- 使用ConfigMap管理数据源配置
- 为每个Pod设置合理的连接池大小
- 实现健康检查接口监控连接状态
编程学习
技术分享
实战经验