1. 项目背景与需求分析
在企业级应用开发中,经常需要同时连接多个不同类型的数据库。最近我在开发一个财务分析系统时,就遇到了需要同时访问PostgreSQL(存储业务数据)和SQL Server(存储历史财务数据)的需求。这种多数据源场景在以下情况特别常见:
- 系统迁移过渡期(新旧数据库并存)
- 需要集成第三方系统数据
- 不同业务模块使用不同数据库技术栈
- 读写分离或分库分表场景
Spring Boot作为Java生态中最流行的应用框架,其自动配置特性让数据库连接变得简单。但官方默认只支持单一数据源配置,要实现多数据源需要一些特殊处理。下面我将分享经过生产验证的完整实现方案。
2. 技术方案设计
2.1 核心实现思路
多数据源的本质是创建多个DataSource实例,并通过不同方式区分访问路径。在Spring Boot中主要有两种实现方式:
- AbstractRoutingDataSource动态路由:通过线程上下文切换数据源
- 独立配置+限定符注入:为每个数据源创建完整配置链
经过对比测试,我选择了第二种方案,因为:
- 代码结构更清晰,便于维护
- 对事务支持更好
- 不同数据源可以使用完全独立的配置策略
- 与MyBatis等ORM框架集成更简单
2.2 技术组件选型
mermaid复制graph TD
A[Spring Boot 2.7.x] --> B[PostgreSQL 42.3.3]
A --> C[SQL Server JDBC 9.4.1]
A --> D[MyBatis Spring 2.2.2]
A --> E[HikariCP 4.0.3]
注意:JDBC驱动版本需要与数据库服务器版本匹配,否则可能出现兼容性问题
3. 详细实现步骤
3.1 基础依赖配置
首先在pom.xml中添加必要依赖:
xml复制<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.3</version>
</dependency>
<!-- SQL Server -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>9.4.1.jre8</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
3.2 PostgreSQL数据源配置
在application.yml中配置第一个数据源:
yaml复制spring:
datasource:
postgres:
jdbc-url: jdbc:postgresql://localhost:5432/biz_db
username: pguser
password: pgpassword
driver-class-name: org.postgresql.Driver
hikari:
pool-name: PG-HikariPool
maximum-pool-size: 10
connection-timeout: 30000
对应的Java配置类:
java复制@Configuration
@MapperScan(
basePackages = "com.example.mapper.pg",
sqlSessionFactoryRef = "pgSqlSessionFactory"
)
public class PostgresConfig {
@Bean
@ConfigurationProperties("spring.datasource.postgres")
public DataSource pgDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
public SqlSessionFactory pgSqlSessionFactory(
@Qualifier("pgDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/pg/*.xml"));
return bean.getObject();
}
@Bean
public DataSourceTransactionManager pgTransactionManager(
@Qualifier("pgDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.3 SQL Server数据源配置
application.yml中第二个数据源配置:
yaml复制spring:
datasource:
sqlserver:
jdbc-url: jdbc:sqlserver://localhost:1433;databaseName=finance_db
username: sa
password: sqlpassword
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
hikari:
pool-name: MSSQL-HikariPool
maximum-pool-size: 5
connection-test-query: SELECT 1
对应的Java配置类:
java复制@Configuration
@MapperScan(
basePackages = "com.example.mapper.mssql",
sqlSessionFactoryRef = "mssqlSqlSessionFactory"
)
public class SqlServerConfig {
@Bean
@ConfigurationProperties("spring.datasource.sqlserver")
public DataSource mssqlDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
public SqlSessionFactory mssqlSqlSessionFactory(
@Qualifier("mssqlDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("com.example.model.mssql");
return bean.getObject();
}
@Bean
public DataSourceTransactionManager mssqlTransactionManager(
@Qualifier("mssqlDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.4 事务管理增强
由于使用了多个TransactionManager,需要通过@Transactional注解指定:
java复制@Service
public class FinanceService {
@Transactional(transactionManager = "pgTransactionManager")
public void businessOperation() {
// 使用PostgreSQL数据源
}
@Transactional(transactionManager = "mssqlTransactionManager")
public void financeOperation() {
// 使用SQL Server数据源
}
}
4. 常见问题与解决方案
4.1 连接池配置优化
问题现象:高并发时出现连接等待超时
解决方案:
yaml复制hikari:
maximum-pool-size: ${DB_POOL_SIZE:10} # 根据CPU核心数调整
minimum-idle: 2
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
leak-detection-threshold: 5000
经验值:Pool Size = (核心数 * 2) + 有效磁盘数
4.2 SQL Server特殊配置
问题1:SSL加密报错
解决:在连接字符串添加加密配置
code复制jdbc:sqlserver://...;encrypt=true;trustServerCertificate=true
问题2:时区不一致
解决:添加时区参数
code复制jdbc:sqlserver://...;sendTimeAsDateTime=false
4.3 PostgreSQL性能调优
java复制@Bean
public DataSource pgDataSource() {
HikariDataSource ds = DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
ds.setConnectionInitSql("SET work_mem = '16MB'");
return ds;
}
5. 高级应用场景
5.1 动态数据源切换
通过AOP实现基于注解的动态切换:
java复制@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "pgDataSource";
}
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(dataSource)")
public void beforeSwitch(DataSource dataSource) {
String dsName = dataSource.value();
DatabaseContextHolder.set(dsName);
}
}
5.2 多数据源事务协调
使用JTA实现分布式事务:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
配置示例:
java复制@Bean
public DataSource pgDataSource() {
PGXADataSource ds = new PGXADataSource();
// 配置XA数据源
return new AtomikosDataSourceBean(ds);
}
6. 生产环境建议
- 连接监控:集成Micrometer监控连接池指标
- 故障转移:配置多节点URL
code复制jdbc:postgresql://host1:5432,host2:5432/db - 密码加密:使用Jasypt加密配置中的密码
- 连接验证:定期执行健康检查SQL
经过实际项目验证,这套方案可以稳定支持每秒500+的数据库操作。关键是要根据业务特点合理设置连接池参数,并做好监控预警。
