Spring Boot 3.0动态多数据源切换实战教程

张开发
2026/6/30 3:24:06 15 分钟阅读
Spring Boot 3.0动态多数据源切换实战教程
在现代企业级应用中数据源切换是应对读写分离、多租户架构或分库分表等场景的常见需求。本教程将详细介绍如何在 Spring Boot 3.0 中通过整合dynamic-datasource-spring-boot3-starter这一成熟的开源方案以最小的代码量实现优雅、高效的多数据源动态切换。一、为什么选择 dynamic-datasource-spring-boot3-starter虽然 Spring 提供了AbstractRoutingDataSource作为底层支持但手动实现一套完整的动态数据源切换机制包括 AOP 切面、ThreadLocal 上下文管理等较为繁琐。dynamic-datasource项目正是为了解决这一痛点而生它基于 Spring Boot 的自动装配机制提供了一套开箱即用的解决方案。核心优势零配置启动通过简单的application.yml配置即可定义多个数据源。注解驱动使用DS注解即可在 Service 层或 Mapper 层轻松切换数据源。功能丰富原生支持读写分离、多数据源组、SpEL 表达式等高级特性。Spring Boot 3 原生支持dynamic-datasource-spring-boot3-starter专门为 Spring Boot 3.x 设计完美兼容 Jakarta EE 规范。二、项目依赖与配置1、添加 Maven 依赖在项目的pom.xml文件中添加dynamic-datasource-spring-boot3-starter依赖。请确保你的 Spring Boot 版本为 3.x。dependencies !-- Spring Boot Web Starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 动态数据源 Starter for Spring Boot 3 -- dependency groupIdcom.baomidou/groupId artifactIddynamic-datasource-spring-boot3-starter/artifactId version4.3.0/version !-- 请使用最新稳定版本 -- /dependency !-- 数据库驱动以 MySQL 为例 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- MyBatis-Plus Starter (可选用于简化数据访问层) -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.5/version /dependency /dependencies2、配置多数据源在src/main/resources/application.yml中进行配置。dynamic前缀下的primary属性用于指定默认数据源datasource下则定义具体的数据源连接信息。spring: datasource: dynamic: # 设置默认数据源未指定 DS 注解时将使用此数据源 primary: master # 开启 SQL 日志打印方便调试 p6spy: true datasource: # 主库配置用于写操作 master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_master?useSSLfalseserverTimezoneUTC username: root password: your_password # 从库配置用于读操作 slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_slave?useSSLfalseserverTimezoneUTC username: root password: your_password三、核心代码实现1、定义数据实体与 Mapper首先创建一个简单的用户实体类User。import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; Data TableName(user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String email; }接着创建对应的 MyBatis-Plus Mapper 接口。import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; Mapper public interface UserMapper extends BaseMapperUser { // 继承 BaseMapper 后已拥有基础的 CRUD 方法 }2、在 Service 层使用 DS 注解DS注解是实现数据源切换的关键。它可以标注在类或方法上方法上的注解优先级更高。import com.baomidou.dynamic.datasource.annotation.DS; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; Service public class UserService { private final UserMapper userMapper; public UserService(UserMapper userMapper) { this.userMapper userMapper; } /** * 写操作使用默认的 master 数据源。 * 由于 application.yml 中 primary 设置为 master因此此处可不加 DS 注解。 */ Transactional public void addUser(User user) { userMapper.insert(user); } /** * 读操作通过 DS(slave) 明确指定使用 slave 数据源。 * 这实现了读写分离减轻主库压力。 */ DS(slave) public ListUser getAllUsers() { return userMapper.selectList(null); } /** * 也可以在类级别上使用 DS 注解 * 那么该类下所有方法默认都会使用指定的数据源。 */ }3、编写 Controller 进行测试创建一个简单的 REST Controller 来暴露接口方便测试。import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/users) public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService userService; } PostMapping public void createUser(RequestBody User user) { userService.addUser(user); } GetMapping public ListUser listUsers() { // 调用此方法将查询从库 return userService.getAllUsers(); } }四、高级特性与最佳实践1、读写分离与负载均衡dynamic-datasource支持更复杂的读写分离配置。你可以定义一个数据源组其中包含一个主库和多个从库。当进行读操作时它会自动在从库间进行负载均衡默认轮询。spring: datasource: dynamic: primary: mydb # 默认数据源组 datasource: mydb: # 定义一个名为 mydb 的组 master: # 组内的主库 url: jdbc:mysql://localhost:3306/db_master... username: root password: password slave1: # 组内的从库1 url: jdbc:mysql://localhost:3306/db_slave1... username: root password: password slave2: # 组内的从库2 url: jdbc:mysql://localhost:3306/db_slave2... username: root password: password使用方式不变DS(mydb)进行写操作时会自动路由到master进行读操作时则会在slave1和slave2之间负载均衡。2、多数据源事务管理这是一个需要特别注意的要点。Transactional注解是基于数据源绑定的。当一个方法被DS和Transactional同时修饰时事务将作用于DS指定的数据源。重要提醒应避免在单个Transactional方法内跨越多个数据源进行操作。Spring 的声明式事务管理器无法保证跨库操作的原子性。如果需要处理跨库业务应考虑使用分布式事务框架如 Seata或将业务逻辑拆分为多个独立的、各自管理单一数据源的事务方法。3、运行时动态管理数据源对于多租户 SaaS 应用可能需要根据租户 ID 动态创建和销毁数据源。dynamic-datasource提供了DynamicDataSourceService接口来实现这一功能。import com.baomidou.dynamic.datasource.DynamicDataSourceService; import com.baomidou.dynamic.datasource.creator.DataSourceProperty; import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; import org.springframework.stereotype.Service; Service public class TenantDataSourceService { private final DynamicDataSourceService dynamicDataSourceService; private final DefaultDataSourceCreator dataSourceCreator; public TenantDataSourceService(DynamicDataSourceService dynamicDataSourceService, DefaultDataSourceCreator dataSourceCreator) { this.dynamicDataSourceService dynamicDataSourceService; this.dataSourceCreator dataSourceCreator; } // 为指定租户添加数据源 public void addTenantDataSource(String tenantId, String jdbcUrl, String username, String password) { DataSourceProperty property new DataSourceProperty(); property.setPoolName(tenantId); // 连接池名称 property.setUrl(jdbcUrl); property.setUsername(username); property.setPassword(password); property.setDriverClassName(com.mysql.cj.jdbc.Driver); // 创建并添加数据源 dynamicDataSourceService.addDataSource(dataSourceCreator.createDataSource(property)); } // 移除指定租户的数据源 public void removeTenantDataSource(String tenantId) { dynamicDataSourceService.removeDataSource(tenantId); } }通过以上步骤你可以在 Spring Boot 3.0 项目中快速、稳定地实现多数据源动态切换满足各种复杂的业务场景需求。

更多文章