spring boot + mysql 项目配置多数据源

本文详细介绍了如何在SpringBoot项目中配置和使用多数据源,包括application.yml配置、数据源注解、数据源切面配置、动态数据源实现等关键步骤。通过具体示例展示了如何在mapper层指定不同数据源,实现数据源的灵活切换。

spring boot 项目如需实现多数据源,进行简单的配置即可

application.yml 文件配置

spring:
  datasource:
    primary: #主数据源
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://xxxxxxx:3306/car?characterEncoding=utf8&serverTimezone=GMT%2B8
      username: root
      password: root

    secondary: #从数据源
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://xxxxxxx:3306/car?characterEncoding=utf8&serverTimezone=GMT%2B8
      username: root
      password: password

目标数据源注解,注解在方法上指定数据源的名称

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {

    //此处接收数据源名称
    String value();
}

数据源切面的配置,我这里是在mapper标注访问不同数据源

@Aspect
@Component
public class DataSourceAspect {

    private static Logger logger = LogManager.getLogger(DataSourceAspect.class.getName());

    //切入点在service层的方法上,配置aop的切入点
    @Pointcut("execution( * com.xxxx.mapper.*.*(..))")
    public void dataSourcePointCut() {
    }

    //切入点只对@Service注解的类上的@DataSource方法生效
//    @Pointcut(value="@within(org.springframework.stereotype.Service) && @annotation(dataSource)" )
//    public void dataSourcePointCut(DataSource dataSource) {
//    }

    @Before("dataSourcePointCut()")
    public void before(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        String method = joinPoint.getSignature().getName();
        Class<?>[] clazz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
        try {
            Method m = clazz[0].getMethod(method, parameterTypes);
            //如果方法上存在切换数据源的注解,则根据注解内容进行数据源切换
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource annotation = m.getAnnotation(DataSource.class);
                String dataSourceName = annotation.value();
                DynamicDataSourceHolder.putDataSouce(dataSourceName);
                logger.debug("-----current thread " + Thread.currentThread().getName() + " add " + dataSourceName + " to ThreadLocal-----");

            } else {
                logger.debug("switch datasource fail, use default");
            }
        } catch (NoSuchMethodException e) {
            logger.error("current thread " + Thread.currentThread().getName() + " add data to ThreadLocal error", e);
        }
    }

    //执行完切面后,清空线程共享中的数据源名称
    @After("dataSourcePointCut()")
    public void after(JoinPoint joinPoint){
        DynamicDataSourceHolder.removeDataSource();
    }
}

动态数据源实现类

@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {

    //数据源路由,用于产生要选取的数据源逻辑名称
    @Override
    protected Object determineCurrentLookupKey() {
        //从线程共享中获取数据源名称
        return DynamicDataSourceHolder.getDataSource();
    }
}

数据源配置,我这里是只配置了两个

@Configuration
@EnableScheduling
@Slf4j
public class DataSourceConfig {
    @Autowired
    private DBProperties dbProperties;

    /**
     * 设置动态数据源,通过@Primary 来确定主DataSource
     */
    @Bean(name = "dataSource")
    public DynamicDataSource dataSource(){
        DynamicDataSource dynamicDataSource = new DynamicDataSource();

        //1.设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dbProperties.getPrimary());
        //2.配置多数据源
        Map<Object, Object> map = new HashMap<>();
        map.put("primary", dbProperties.getPrimary());
        map.put("secondary", dbProperties.getSecondary());
        //3.存放数据源集
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }
}

动态数据源持有者,负责利用ThreadLocal存取数据源名称

public class DynamicDataSourceHolder {

    //本地线程共享对象
    private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();

    public static void putDataSouce(String name){
        THREAD_LOCAL.set(name);
    }

    public static String getDataSource(){
        return THREAD_LOCAL.get();
    }

    public static void removeDataSource(){
        THREAD_LOCAL.remove();
    }
}

数据源实体类

@Component
@Data
@ConfigurationProperties(prefix = "spring.datasource")
public class DBProperties {

    private HikariDataSource primary;
    private HikariDataSource secondary;

    public HikariDataSource getPrimary() {
        return primary;
    }

    public void setPrimary(HikariDataSource primary) {
        this.primary = primary;
    }

    public HikariDataSource getSecondary() {
        return secondary;
    }

    public void setSecondary(HikariDataSource secondary) {
        this.secondary = secondary;
    }
}

测试实现,在mapper中指定不同数据源

@DataSource("primary")
List<Agent> getPrimary();

@DataSource("secondary")
List<Agent> getSecondary();

启动项目访问接口,即可获取不同数据源的数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值