1. 多数据源场景下的Spring Boot应用架构设计
在企业级应用开发中,多数据源连接是常见的需求场景。当我们需要同时操作PostgreSQL和SQL Server这两种不同类型的数据库时,传统的单数据源配置显然无法满足需求。Spring Boot通过其灵活的自动配置机制,为我们提供了优雅的多数据源解决方案。
PostgreSQL作为功能强大的开源关系型数据库,常被用于存储业务核心数据;而SQL Server作为微软的企业级数据库产品,许多传统系统仍在使用。两者混合使用的典型场景包括:
- 新旧系统迁移过渡期
- 跨部门数据整合(如财务系统用SQL Server,业务系统用PostgreSQL)
- 特定功能需求(如PostgreSQL的JSONB类型与SQL Server的存储过程各有所长)
在技术实现层面,多数据源配置需要解决三个核心问题:
- 如何区分不同数据源的连接配置
- 如何隔离各自的ORM操作上下文
- 如何管理跨数据源的事务一致性
2. 项目环境搭建与依赖配置
2.1 初始化Spring Boot项目
使用IDEA或Spring Initializr创建项目时,基础依赖选择:
- Spring Web (提供Web MVC支持)
- Lombok (简化实体类编写)
- Spring Boot DevTools (开发热部署)
xml复制<!-- 基础POM依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
</parent>
2.2 数据库驱动选择
对于PostgreSQL,建议使用最新稳定版驱动。SQL Server驱动有两个选择:
- 微软官方驱动(mssql-jdbc)
- JTDS开源驱动(已停止维护)
xml复制<!-- PostgreSQL驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<!-- SQL Server官方驱动 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.2.0.jre11</version>
</dependency>
2.3 MyBatis-Plus集成
MyBatis-Plus 3.5.x版本对多数据源支持良好,需注意:
- 每个数据源需要独立的Mapper扫描配置
- 分页插件等需要分别初始化
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
3. 多数据源核心配置实现
3.1 YAML配置规范
在application.yml中,采用自定义前缀区分数据源。关键配置项包括:
- 连接URL格式差异
- 驱动类名指定
- 连接池参数调优
yaml复制spring:
datasource:
pg:
jdbc-url: jdbc:postgresql://localhost:5432/pg_db?currentSchema=public
username: postgres
password: 123456
driver-class-name: org.postgresql.Driver
hikari:
connection-timeout: 30000
maximum-pool-size: 15
sqlserver:
jdbc-url: jdbc:sqlserver://localhost:1433;databaseName=ms_db;encrypt=true;trustServerCertificate=true
username: sa
password: Sql@1234
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
hikari:
connection-timeout: 40000
maximum-pool-size: 10
注意:SQL Server的encrypt参数在生产环境应配置正式证书,开发环境可暂时使用trustServerCertificate简化配置
3.2 PostgreSQL数据源配置类
主数据源需要添加@Primary注解,关键配置点:
- 指定Mapper扫描包路径
- 配置独立的SqlSessionFactory
- 初始化事务管理器
java复制@Configuration
@MapperScan(basePackages = "com.example.mapper.pg",
sqlSessionFactoryRef = "pgSqlSessionFactory")
public class PgDataSourceConfig {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.pg")
public DataSource pgDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean
public SqlSessionFactory pgSqlSessionFactory(
@Qualifier("pgDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage("com.example.entity.pg");
// MyBatis-Plus插件配置
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
factory.setPlugins(interceptor);
return factory.getObject();
}
@Primary
@Bean
public DataSourceTransactionManager pgTransactionManager(
@Qualifier("pgDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.3 SQL Server数据源配置类
次数据源配置与主数据源类似,但需要注意:
- 移除@Primary注解
- 指定不同的Mapper扫描路径
- 分页插件使用SQL Server方言
java复制@Configuration
@MapperScan(basePackages = "com.example.mapper.sqlserver",
sqlSessionFactoryRef = "sqlServerSqlSessionFactory")
public class SqlServerDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.sqlserver")
public DataSource sqlServerDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory sqlServerSqlSessionFactory(
@Qualifier("sqlServerDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setTypeAliasesPackage("com.example.entity.sqlserver");
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQL_SERVER));
factory.setPlugins(interceptor);
return factory.getObject();
}
@Bean
public DataSourceTransactionManager sqlServerTransactionManager(
@Qualifier("sqlServerDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
4. 业务层实现与事务管理
4.1 Service层代码组织
推荐按数据源划分包结构,避免混淆:
code复制src/main/java/com/example/
├── service
│ ├── pg
│ │ └── UserService.java
│ └── sqlserver
│ └── OrderService.java
└── config
跨数据源Service示例:
java复制@Service
@RequiredArgsConstructor
public class CrossDbService {
private final PgUserMapper pgUserMapper;
private final SqlServerOrderMapper sqlServerOrderMapper;
// 默认使用主数据源事务
@Transactional
public void createUserWithOrder(User user, Order order) {
pgUserMapper.insert(user);
// 需要指定次数据源事务管理器
createOrderInNewTransaction(order);
}
@Transactional(transactionManager = "sqlServerTransactionManager")
public void createOrderInNewTransaction(Order order) {
sqlServerOrderMapper.insert(order);
}
}
4.2 分布式事务处理方案
对于严格的ACID要求,可考虑:
- JTA+Atomikos方案
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
- Seata分布式事务框架
yaml复制# application.yml
seata:
enabled: true
application-id: multi-ds-app
tx-service-group: my_tx_group
4.3 性能优化建议
- 连接池参数调优:
yaml复制spring:
datasource:
pg:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 600000
max-lifetime: 1800000
- MyBatis二级缓存配置:
java复制@Bean
public ConfigurationCustomizer mybatisConfigurationCustomizer() {
return configuration -> {
configuration.setCacheEnabled(true);
configuration.setLazyLoadingEnabled(false);
};
}
5. 常见问题排查与解决方案
5.1 连接初始化失败
PostgreSQL连接问题:
- 错误现象:
Connection to localhost:5432 refused - 检查项:
- pg_hba.conf文件配置
- 防火墙端口开放情况
- 数据库用户权限
SQL Server连接问题:
- 典型错误:
The TCP/IP connection to the host failed - 解决方案:
- 启用SQL Server配置管理器中的TCP/IP协议
- 检查SQL Server Browser服务是否运行
- 确认混合身份验证模式已启用
5.2 事务不生效场景
- 自调用问题:
java复制public void methodA() {
methodB(); // 事务注解失效
}
@Transactional
public void methodB() {}
- 异常被捕获:
java复制@Transactional
public void process() {
try {
// 业务代码
} catch (Exception e) {
// 异常被吃掉,事务不会回滚
}
}
5.3 MyBatis-Plus兼容性问题
- 分页插件版本冲突:
- 症状:分页查询返回所有记录
- 解决:确保所有数据源使用相同版本的MyBatis-Plus
- 主键生成策略:
java复制// PostgreSQL使用序列
@TableId(type = IdType.INPUT)
private Long id;
// SQL Server使用自增
@TableId(type = IdType.AUTO)
private Integer id;
6. 生产环境部署建议
6.1 安全配置强化
- 密码加密:
yaml复制spring:
datasource:
pg:
password: ${DB_PASSWORD}
- SSL连接配置:
yaml复制jdbc-url: jdbc:postgresql://host/db?ssl=true&sslmode=verify-full
6.2 监控集成
- Spring Boot Actuator:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- Prometheus监控指标:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
6.3 高可用设计
- 数据库集群配置:
yaml复制# PostgreSQL主从配置
jdbc-url: jdbc:postgresql://primary,secondary/db?targetServerType=primary
# SQL Server AlwaysOn
jdbc-url: jdbc:sqlserver://listenerName;databaseName=db;multiSubnetFailover=true
- 连接池故障转移:
java复制@Bean
@ConfigurationProperties(prefix = "spring.datasource.pg")
public DataSource pgDataSource() {
HikariDataSource ds = DataSourceBuilder.create().type(HikariDataSource.class).build();
ds.setConnectionInitSql("SELECT 1");
ds.setConnectionTestQuery("SELECT 1");
return ds;
}
在实际项目中使用多数据源时,我强烈建议建立完善的文档记录每个数据源的用途、维护人员和变更历史。对于关键业务操作,应当添加详细日志记录数据源切换情况。一个实用的技巧是在日志MDC中记录当前使用的数据源标识,便于问题排查:
java复制@Around("@annotation(targetDataSource)")
public Object switchDataSource(ProceedingJoinPoint pjp, TargetDataSource targetDataSource) {
String dsKey = targetDataSource.value();
MDC.put("dataSource", dsKey);
try {
return pjp.proceed();
} finally {
MDC.remove("dataSource");
}
}
