introduction
In microservice architecture and complex business scenarios, the need for a Spring Boot application to connect multiple databases is becoming increasingly common. Many developers try to implement multiple data sources by simply copying a single data source configuration, but they encounter hidden problems such as bean conflicts, transaction failures, and connection leakage. This article will thoroughly analyze the underlying logic of Spring Boot automatic configuration, reveal typical pitfalls in multi-data source scenarios, and provide a set of production-level solutions.
1. Why does a simple multi-data source configuration fail?
1. Spring Boot's automatic configuration trap
Spring Boot automatically configures single data sources through DataSourceAutoConfiguration by default. The following issues break out when the developer tries to add a second data source:
// Typical misconfiguration method@Bean public DataSource dataSource1() { /* Configuration 1 */ } @Bean public DataSource dataSource2() { /* Configuration 2 */ } // An error occurred when starting:// No qualifying bean of type '' available: // expected single matching bean but found 2
2. "Schrödinger State" of Transaction Management
Even if the data source is successfully injected, a transaction manager not configured correctly can cause:
- Lack of atomicity in cross-data source operations
- @Transactional annotation mysteriously invalid
- Some operations do not roll back
2. The core contradictions of multi-data source configuration
1. Automatically configured "domineering" behavior
Spring Boot's automatic configuration class controls bean creation through conditional annotations:
@Configuration @ConditionalOnClass({ , }) @ConditionalOnMissingBean() // Key points!@EnableConfigurationProperties() public class DataSourceAutoConfiguration { ... }
When multiple DataSource are manually declared, the automatic configuration is disabled, but related components such as JdbcTemplate still rely on the default data source.
2. "Exclusiveness" of the transaction manager
PlatformTransactionManager binds the main data source by default, and multiple data sources require an independent transaction manager:
@Bean @Primary // The main transaction manager must be specified explicitlypublic PlatformTransactionManager txManager1(DataSource dataSource1) { return new DataSourceTransactionManager(dataSource1); }
3. Production-level multi-data source configuration solution
Step 1: Disable the default data source automatic configuration
@SpringBootApplication(exclude = { , , }) public class MultiDataSourceApp { ... }
Step 2: Manually define all data sources
# primary: datasource: url: jdbc:mysql://primary/db username: admin password: pwd123 secondary: datasource: url: jdbc:mysql://secondary/db username: reader password: read123 @Configuration public class DataSourceConfig { // Master data source (must be marked @Primary) @Bean(name = "primaryDataSource") @Primary @ConfigurationProperties(prefix = "") public DataSource primaryDataSource() { return ().build(); } // From the data source @Bean(name = "secondaryDataSource") @ConfigurationProperties(prefix = "") public DataSource secondaryDataSource() { return ().build(); } }
Step 3: Configure an independent transaction manager for each data source
@Configuration public class TransactionManagerConfig { @Bean(name = "primaryTransactionManager") @Primary public PlatformTransactionManager primaryTxManager( @Qualifier("primaryDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "secondaryTransactionManager") public PlatformTransactionManager secondaryTxManager( @Qualifier("secondaryDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
Step 4: Customize JdbcTemplate
@Bean(name = "primaryJdbcTemplate") public JdbcTemplate primaryJdbcTemplate( @Qualifier("primaryDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean(name = "secondaryJdbcTemplate") public JdbcTemplate secondaryJdbcTemplate( @Qualifier("secondaryDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); }
4. Advanced control of multi-data source transactions
1. Pseudo-proposition of distributed transactions
Without introducing middleware such as Seata, Spring's @Transactional can only guarantee the atomicity of a single data source. Cross-store operations require a business-level compensation mechanism.
2. Precise control of transaction propagation
// Identify which transaction manager to use@Transactional(value = "secondaryTransactionManager", propagation = Propagation.REQUIRES_NEW) public void batchInsert() { // Use secondary data source to perform operations}
V. Performance optimization and monitoring
1. Connection pool parameter tuning
@Bean(name = "primaryDataSource") @ConfigurationProperties(prefix = "") public DataSource primaryDataSource() { return () .type().build(); } // primary: datasource: hikari: maximum-pool-size: 20 connection-timeout: 3000
2. Monitoring indicators are exposed
@Bean public DataSourcePoolMetrics primaryDataSourceMetrics( @Qualifier("primaryDataSource") DataSource dataSource) { return new DataSourcePoolMetrics(dataSource, "primary", ()); }
6. Summary and best practices
Strict isolation configuration: Each data source's attribute prefix, bean name, and transaction manager must be clearly isolated.
Explanatory exclusion of automatic configurations: avoid conflicts caused by residual configurations
Definitely transaction boundaries: precisely controlled through @Qualifier and @Transactional properties
Monitoring first: Configure connection pool monitoring to prevent leaks and performance bottlenecks
This is the end of this article about the ultimate solution for SpringBoot multi-data source configuration. For more related SpringBoot multi-data source configuration content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!