在企业级应用开发中,多数据源连接是常见需求。我最近在一个宠物电商平台项目中就遇到了这样的场景:需要同时操作MySQL主从库进行读写分离,还要连接SQL Server处理历史订单数据。经过技术调研,最终选择了SpringBoot+MyBatisPlus+dynamic-datasource的方案组合。
为什么选择这个技术栈?首先,MyBatisPlus是国内Java开发者最熟悉的ORM框架之一,它基于MyBatis进行了功能增强,提供了通用的CRUD操作。而dynamic-datasource是MyBatisPlus官方推荐的多数据源组件,具有以下优势:
提示:对于简单的多数据源需求,也可以考虑Spring自带的AbstractRoutingDataSource方案。但dynamic-datasource在功能完整性和易用性上更胜一筹。
根据项目需求,我们需要准备以下数据库环境:
创建测试数据库的SQL脚本如下:
sql复制-- MySQL主库
CREATE DATABASE `jialiangkj-pet` DEFAULT CHARACTER SET utf8mb4;
USE `jialiangkj-pet`;
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`remark` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `test` VALUES (1, '哈士奇', '这是一个正式库');
-- MySQL从库
CREATE DATABASE `jialiangkj-pet-test` DEFAULT CHARACTER SET utf8mb4;
USE `jialiangkj-pet-test`;
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`remark` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `test` VALUES (1, '边牧', '这是一个测试库');
-- SQL Server
CREATE DATABASE test;
GO
USE test;
GO
CREATE TABLE test.dbo.test (
id int NOT NULL PRIMARY KEY,
name varchar(20) NULL,
remark varchar(50) NULL
);
INSERT INTO test.dbo.test VALUES (1, '柯基', '这是sqlserver数据库');
确保本地开发环境满足以下条件:
在父pom.xml中定义版本管理:
xml复制<properties>
<dynamic-ds.version>3.5.2</dynamic-ds.version>
<mybatis-plus.version>3.5.4</mybatis-plus.version>
<mysql.version>8.0.33</mysql.version>
<sqlserver.version>12.4.2.jre8</sqlserver.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 多数据源核心 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>${sqlserver.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
在业务模块中添加运行时依赖:
xml复制<dependencies>
<!-- 多数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
</dependencies>
application.yml配置示例:
yaml复制spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
dynamic:
primary: master # 默认数据源
strict: true # 严格模式,未匹配数据源时报错
datasource:
master: # 主库
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.3.220:3306/jialiangkj-pet?useSSL=true&serverTimezone=Asia/Shanghai
username: root
password: lvdamaoluguo
slave: # 从库
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.88.170:3306/jialiangkj-pet-test?useSSL=true&serverTimezone=Asia/Shanghai
username: root
password: lvdamaoluguo
sqlserver: # SQL Server
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://localhost:1433;DatabaseName=test
username: sa
password: lvdamaoluguo
hikari:
maxPoolSize: 20
minIdle: 10
connectionTimeout: 30000
注意:生产环境密码应该使用加密配置,推荐使用Jasypt等工具进行加密处理
使用MyBatisX插件自动生成基础代码:
java复制@Data
@TableName("test")
public class TestEntity {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String remark;
}
public interface TestMapper extends BaseMapper<TestEntity> {
}
java复制public interface TestService extends IService<TestEntity> {
TestEntity getFromMaster();
TestEntity getFromSlave();
TestEntity getFromSqlServer();
}
@Service
public class TestServiceImpl extends ServiceImpl<TestMapper, TestEntity>
implements TestService {
@DS("master")
public TestEntity getFromMaster() {
return getById(1);
}
@DS("slave")
public TestEntity getFromSlave() {
return getById(1);
}
@DS("sqlserver")
public TestEntity getFromSqlServer() {
return getById(1);
}
}
java复制@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private TestService testService;
@GetMapping("/master")
public TestEntity master() {
return testService.getFromMaster();
}
@GetMapping("/slave")
public TestEntity slave() {
return testService.getFromSlave();
}
@GetMapping("/sqlserver")
public TestEntity sqlserver() {
return testService.getFromSqlServer();
}
}
多数据源环境下的事务管理需要特别注意:
java复制@DS("master")
@Transactional
public void updateMaster(TestEntity entity) {
updateById(entity);
}
除了注解方式,还可以编程式切换数据源:
java复制DynamicDataSourceContextHolder.push("slave");
try {
// 执行从库操作
return testService.getFromSlave();
} finally {
DynamicDataSourceContextHolder.poll();
}
连接池配置优化:
SQL优化:
可能原因:
解决方案:
常见错误:
解决方案:
版本冲突表现:
解决方案:
数据库连接安全:
监控与告警:
高可用方案:
在实际项目中,我们还将多数据源配置与Nacos配置中心集成,实现了动态刷新数据源配置而不需要重启应用。这需要在dynamic-datasource的基础上进行二次开发,主要思路是监听配置变更事件并重新初始化数据源。