Spring Boot 3.4.0 结合 Mybatis-plus 实现动态数据源的完整方案
作者:Joyous
本文详细介绍了在 Spring Boot 3.4.0 项目中结合 Mybatis-plus 实现动态数据源切换的完整方案,通过自定义注解和AOP切面,我们可以优雅地实现方法级别的数据源切换,满足多数据源场景下的各种需求,感兴趣的朋友一起看看吧
前言
在实际企业级应用开发中,多数据源的需求非常常见。本文将详细介绍如何在 Spring Boot 3.4.0 项目中结合 Mybatis-plus 实现动态数据源切换功能。
一、环境准备
首先确保你的开发环境满足以下要求:
- JDK 17+
- Spring Boot 3.4.0
- Mybatis-plus 3.5.3.1+
- Maven 3.6.3+
二、项目配置
1. 添加依赖
在 pom.xml 中添加以下依赖:
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 其他工具 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>2. 配置文件
在 application.yml 中配置多个数据源:
spring:
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
slave1:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/slave1_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
slave2:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/slave2_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456三、实现动态数据源
1. 数据源枚举
public enum DataSourceType {
MASTER("master"),
SLAVE1("slave1"),
SLAVE2("slave2");
private final String name;
DataSourceType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}2. 动态数据源上下文
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}3. 动态数据源配置
@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.getName(), masterDataSource());
targetDataSources.put(DataSourceType.SLAVE1.getName(), slave1DataSource());
targetDataSources.put(DataSourceType.SLAVE2.getName(), slave2DataSource());
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
return dynamicDataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}4. 动态数据源实现
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}5. 自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
DataSourceType value() default DataSourceType.MASTER;
}6. AOP 切面实现
@Aspect
@Component
@Order(-1)
@Slf4j
public class DataSourceAspect {
@Pointcut("@annotation(com.example.annotation.DataSource)" +
"|| @within(com.example.annotation.DataSource)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
DataSource dataSource = getDataSource(point);
if (dataSource != null) {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().getName());
}
try {
return point.proceed();
} finally {
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
private DataSource getDataSource(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
if (dataSource != null) {
return dataSource;
}
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}
}四、使用示例
1. Service 层使用
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@DataSource(DataSourceType.MASTER)
public User getMasterUser(Long id) {
return userMapper.selectById(id);
}
@Override
@DataSource(DataSourceType.SLAVE1)
public User getSlave1User(Long id) {
return userMapper.selectById(id);
}
@Override
@DataSource(DataSourceType.SLAVE2)
public User getSlave2User(Long id) {
return userMapper.selectById(id);
}
}2. Controller 层调用
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/master/{id}")
public User master(@PathVariable Long id) {
return userService.getMasterUser(id);
}
@GetMapping("/slave1/{id}")
public User slave1(@PathVariable Long id) {
return userService.getSlave1User(id);
}
@GetMapping("/slave2/{id}")
public User slave2(@PathVariable Long id) {
return userService.getSlave2User(id);
}
}五、注意事项
- 事务管理:动态数据源切换与事务管理需要特别注意,建议在事务方法中不要切换数据源
- 连接池配置:可以为每个数据源单独配置连接池参数
- 性能考虑:频繁切换数据源可能会影响性能,应根据实际需求合理设计
- 异常处理:做好数据源切换失败时的异常处理
六、总结
本文详细介绍了在 Spring Boot 3.4.0 项目中结合 Mybatis-plus 实现动态数据源切换的完整方案。通过自定义注解和AOP切面,我们可以优雅地实现方法级别的数据源切换,满足多数据源场景下的各种需求。
到此这篇关于Spring Boot 3.4.0 结合 Mybatis-plus 实现动态数据源的完整方案的文章就介绍到这了,更多相关Spring Boot Mybatis-plus动态数据源内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
