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);
        }
    }

posted on 2026-08-10 15:34  爱河  阅读(2)  评论(0)    收藏  举报

导航