SpringBoot多数据源

Mybatis每次操作数据库时都会先获取当前数据源,spring-jdbc模块提AbstractRoutingDataSource让我们指定数据源,因此我们可以动态的指定数据源。

一、设置数据源,spring-jdbc模块提AbstractRoutingDataSource让我们指定数据源,我们实现其determineCurrentLookupKey()方法返回一个我们需要的数据源即可;

/**
 * 动态数据源类
 *
 * @author hmh
 */
public class DynamicDatasource extends AbstractRoutingDataSource {
    /**
     * 这里的determineCurrentLookupKey方法,需要返回一个数据源,也就是说返回一个数据源的映射,
     * 这里返回一个DynamicDatasourceHolder.getDataSource()方法的返回值,DynamicDatasourceHolder是一个保存多个数据源的地方
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDatasourceHolder.getDataSource();
    }
}

二、创建DynamicDatasourceHolder工具类,这里我使用的是ThreadLocal。

/**
 * 多数据源工具类、使用ThreadLocal保证一个线程使用一个数据源
 *
 * @author hmh
 */
public class DynamicDatasourceHolder {
    private static final ThreadLocal<String> LOCAL = new ThreadLocal<>();

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

    public static void setDataSource(String dataSourceKey) {
        LOCAL.set(dataSourceKey);
    }

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

三、注册多数据源(将配置文件中配置的多数据源注册到DynamicDataSource中)

1,获取多数据源配置类

/**
 * 多数据源配置
 *
 * @author hmh
 */
@Configuration
@ConfigurationProperties(prefix = "spring.dynamic")
@Data
public class DynamicDataSourceProperties {
    /**
     * 多数据源
     */
    private List<Map<String, DataSourceConfig>> datasource;

    /**
     * 默认数据源(不默认数据源且找不到指定数据源时报错)
     */
    private String defaultDatasource;

    @Data
    public static class DataSourceConfig {
        private String jdbcUrl;
        private String username;
        private String password;
        private String driverClassName;
    }
}

2,注册多数据源(配置文件中配置多少个数据源就注册多少数据源,注册数据源的名称为配置文件中数据源的key,与网上大多数写死数据源不相同,本文章出发点为"公共")

/**
 * 多数据源配置类
 *
 * @author hmh
 */
@Configuration
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
@RequiredArgsConstructor
public class DynamicDatasourceConfiguration {
    private final DynamicDataSourceProperties properties;

    @Bean
    @Primary
    public DataSource dataSource() {
        Map<Object, Object> dataSourceMap = new HashMap<>(16);

        List<Map<String, DynamicDataSourceProperties.DataSourceConfig>> datasource = properties.getDatasource();
        String defaultDatasource = properties.getDefaultDatasource();

        if (Func.isEmpty(datasource)) {
            throw new ServiceException("请配置多数据源");
        }
        DynamicDatasource dynamicDatasource = new DynamicDatasource();

        datasource.forEach(item -> item.forEach((datasourceName, dataSource) -> {
            DataSourceBuilder<?> builder = DataSourceBuilder.create();
            builder.url(dataSource.getJdbcUrl());
            builder.username(dataSource.getUsername());
            builder.password(dataSource.getPassword());
            builder.driverClassName(dataSource.getDriverClassName());

            DataSource build = builder.build();
            dataSourceMap.put(datasourceName, build);

            if (Func.isNotBlank(defaultDatasource) && Func.equals(defaultDatasource, datasourceName)) {
                dynamicDatasource.setDefaultTargetDataSource(build);
            }
        }));

        dynamicDatasource.setTargetDataSources(dataSourceMap);
        return dynamicDatasource;
    }

}

四、定义注解以及切面,实现根据注解配置切换数据源

1,定义注解

/**
 * 动态数据源的注解
 * 用在类和方法上,方法上的优先级大于类上的
 * 默认值是master
 *
 * @author hmh
 * @date 2023/12/6 16:19
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DDS {
    String value() default CommonConstant.MASTER;
}

2,注解切面(使用ThreadLocal注意在方法执行完成后清空ThreadLocal避免内存溢出)

/**
 * 动态数据源切面
 *
 * @author hmh
 */
@Aspect
@Component
@Order(value = Integer.MIN_VALUE)
public class DynamicDatasourceAspect {
    /**
     * 切点,切的是带有@DDS注解的方法或类
     */
    @Pointcut("@annotation(com.assets.dynamic.annotation.DDS) || @within(com.assets.dynamic.annotation.DDS)")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String datasourceKey;

        // 方法上的注解
        DDS annotationMethod = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(DDS.class);
        if (Func.notNull(annotationMethod)) {
            datasourceKey = annotationMethod.value();
        } else {
            // 类上的注解
            datasourceKey = joinPoint.getTarget().getClass().getAnnotation(DDS.class).value();
        }
        // 设置数据源
        DynamicDatasourceHolder.setDataSource(datasourceKey);
        try {
            return joinPoint.proceed();
        } finally {
            DynamicDatasourceHolder.removeDataSource();
        }
    }
}

五、配置示例

spring:
  # 多数据源配置
  dynamic:
    # 默认的数据源
    default-datasource: master
    datasource:
      # 数据源名称
      - master:
          jdbc-url: jdbc:mysql://localhost:3306/XXX?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
      - slave:
          jdbc-url: jdbc:mysql://localhost:3306/XXX1?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值