SpringBoot多数据源应用案例
一、前言
在实际的开发工作中,我们经常会遇到需要整合多个数据源的情况,比如同时连接多个数据库、读写分离、跨数据库查询等。本文将介绍如何使用Spring Boot来实现多数据源的整合,对于刚刚接触开发的小伙伴可能有一些帮助。
1、pom依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>
2、DataSource yml配置
spring.datasource.dynamic.primary=master
spring.datasource.dynamic.datasource.master.username=root
spring.datasource.dynamic.datasource.master.password=ENC(tGSy6ZxLDvmyph2fhXGKTIk4DPF28/E1eSdBw08PWEM=)
spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.dynamic.datasource.master.url=jdbc:mysql://127.0.0.1:3399/pure?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.engine.username=root
spring.datasource.dynamic.datasource.engine.password=ENC(tGSy6ZxLDvmyph2fhXGKTIk4DPF28/E1eSdBw08PWEM=)
spring.datasource.dynamic.datasource.engine.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.dynamic.datasource.engine.url=jdbc:mysql://127.0.0.1:3399/pure?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&allowMultiQueries=true&useSSL=false
3、DataSourceConfig配置
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @author wzh
* @create 2022-11-15 15:58
* @Deprecated
*/
@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class DataSourceConfig {
@Bean(DataSourceType.MASTER)
@ConfigurationProperties("spring.datasource.dynamic.datasource.master")
public DataSource master() {
return DruidDataSourceBuilder.create().build();
}
@Bean(DataSourceType.SLAVE)
@ConfigurationProperties("spring.datasource.dynamic.datasource.engine")
public DataSource engine() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DataSource dataSource(){
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(DataSourceType.MASTER, master());
dataSourceMap.put(DataSourceType.SLAVE, engine());
DynamicDatasource dynamicDatasource=new DynamicDatasource();
dynamicDatasource.setTargetDataSources(dataSourceMap);
dynamicDatasource.setDefaultTargetDataSource(master());
return dynamicDatasource;
}
}
4、DataSourceType配置
/**
* @author wno5974
* @create 2023-09-20 15:07
* @Deprecated
*/
public class DataSourceType {
public static final String MASTER = "master";
public static final String SLAVE = "engine";
}
5、DynamicDatasource配置
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源
* @date 2023/6/8 19:18
*/
public class DynamicDatasource extends AbstractRoutingDataSource {
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDatasourceHolder.getDataSource();
}
}
6、DynamicDatasourceHolder配置
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
/**
* @date 2023/6/8 19:42
*/
public class DynamicDatasourceHolder {
//保存数据源的映射
private static Queue<String> queue = new ArrayBlockingQueue<String>(1);
public static String getDataSource() {
return queue.peek();
}
public static void setDataSource(String dataSourceKey) {
queue.add(dataSourceKey);
}
public static void removeDataSource(String dataSourceKey) {
queue.remove(dataSourceKey);
}
}
7、注解配置
import com.won.pure.config.DataSourceType;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**动态数据源的注解
* 用在类和方法上,方法上的优先级大于类上的
* 默认值是master
* @date 2023/6/9 16:19
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DDS {
String value() default DataSourceType.MASTER;
}
8、AOP切面编程,动态切换数据源
import com.won.pure.anntion.DDS;
import com.won.pure.config.DataSourceType;
import com.won.pure.config.DynamicDatasourceHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Objects;
@Aspect
@Order(0)
@Component
@Slf4j
public class DataSourceAspect {
/**
* 切点,切的是带有@DDS的注解
*/
@Pointcut("@annotation(com.won.pure.anntion.DDS)")
public void dynamicDatasourcePointcut(){
}
@Around("dynamicDatasourcePointcut()")
public Object around(ProceedingJoinPoint joinPoint)throws Throwable{
String datasourceKey=DataSourceType.MASTER;
//类上的注解
Class<?> targetClass=joinPoint.getTarget().getClass();
DDS annotation=targetClass.getAnnotation(DDS.class);
//方法上的注解
MethodSignature methodSignature=(MethodSignature)joinPoint.getSignature();
DDS annotationMethod=methodSignature.getMethod().getAnnotation(DDS.class);
if(Objects.nonNull(annotationMethod)){
datasourceKey=annotationMethod.value();
}else{
datasourceKey=annotation.value();
}
//设置数据源
DynamicDatasourceHolder.setDataSource(datasourceKey);
try{
return joinPoint.proceed();
}finally {
String dataSource = DynamicDatasourceHolder.getDataSource();
log.info("数据源===》"+dataSource);
DynamicDatasourceHolder.removeDataSource(datasourceKey);
}
}
}
9、实战案例
/**
* 保存部门管理数据
*
* @return
*/
@DDS(DataSourceType.MASTER)
@PreventSubmit
@Redis
@RequestMapping("saveSysDept")
@ResponseBody
public Result saveSysDept(@RequestBody SysDept sysDept) {
String username = MySpringUtil.getTokenUserName();
String imageUrl = imgView + sysDept.getImageUrl();
sysDept.setImageUrl(imageUrl);
sysDept.setCreator(username);
if(StringUtils.isEmpty(sysDept.getParentId())){
sysDept.setParentId("99");
}
boolean save = sysDeptService.save(sysDept);
if (save) {
return Result.ok(Collections.EMPTY_LIST);
} else {
return Result.fail(Collections.EMPTY_LIST);
}
}
/**
* 修改部门管理数据
*
* @return
*/
@DDS(DataSourceType.SLAVE)
@PreventSubmit
@Redis
@RequestMapping("updateSysDept")
@ResponseBody
public Result updateSysDept(@RequestBody SysDept sysDept) {
boolean save = sysDeptService.updateById(sysDept);
if (save) {
return Result.ok(Collections.EMPTY_LIST);
} else {
return Result.fail(Collections.EMPTY_LIST);
}
}
浙公网安备 33010602011771号