Java —— @DS切换数据源(在同一类 / 方法中访问不同的数据库)

本文介绍如何在Java应用中实现多数据源的动态切换,包括配置动态数据源starter、使用@DS注解来指定数据源,以及集成Druid进行性能监控的方法。

问题

如何在同一个java文件中自由切换,访问不同的数据源?

适用情况

不同数据库之间的数据库传输

不同数据库之间的数据核对

。。。

解决

MyBatis-Plus多数据源官方介绍

一定要看下面这个文件!

GitHub动态数据源

步骤

引入dynamic-datasource-spring-boot-starter。 

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

配置数据源。

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
      datasource:
        master:
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        slave_2:
          url: ENC(xxxxx) # 内置加密,使用请查看详细文档
          username: ENC(xxxxx)
          password: ENC(xxxxx)
          driver-class-name: com.mysql.jdbc.Driver
          schema: db/schema.sql # 配置则生效,自动初始化表结构
          data: db/data.sql # 配置则生效,自动初始化数据
          continue-on-error: true # 默认true,初始化失败是否继续
          separator: ";" # sql默认分号分隔符
          
       #......省略
       #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2

 

# 多主多从                      纯粹多库(记得设置primary)                   混合配置
spring:                               spring:                               spring:
  datasource:                           datasource:                           datasource:
    dynamic:                              dynamic:                              dynamic:
      datasource:                           datasource:                           datasource:
        master_1:                             mysql:                                master:
        master_2:                             oracle:                               slave_1:
        slave_1:                              sqlserver:                            slave_2:
        slave_2:                              postgresql:                           oracle_1:
        slave_3:                              h2:                                   oracle_2:

 

使用 @DS 切换数据源。

@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解

注解结果
没有@DS默认数据源
@DS("dsName")

dsName可以为组名也可以为具体某个库的名称

 

@Service
@DS("slave")
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List selectAll() {
    return  jdbcTemplate.queryForList("select * from user");
  }
  
  @Override
  @DS("slave_1")
  public List selectByCondition() {
    return  jdbcTemplate.queryForList("select * from user where age >10");
  }
}

集成Druid

项目引入 druid-spring-boot-starter 依赖。 

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.1</version>
</dependency>

 

排除原生Druid的快速配置类。

@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

 

某些springBoot的版本上面可能无法排除可用以下方式排除。

spring:
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

 

 

 

 

### Java 动态切换数据源方法 在现代企业级应用中,使用多个数据源来满足不同需求非常普遍。为了提高性能、实现负载均衡、支持多租户架构或灾备恢复等功能,动态切换数据源成为一项重要技能[^1]。 #### 配置文件设置 通常情况下,可以通过配置文件定义多种数据源属性。例如,在 `application.yml` 或者 `application.properties` 文件中指定各个数据源的相关参数: ```yaml spring: datasource: primary: url: jdbc:mysql://localhost:3306/primary_db?useSSL=false&serverTimezone=UTC username: root password: secret driver-class-name: com.mysql.cj.jdbc.Driver secondary: url: jdbc:mysql://localhost:3306/secondary_db?useSSL=false&serverTimezone=UTC username: user password: pass driver-class-name: com.mysql.cj.jdbc.Driver ``` #### 创建自定义的数据源上下文管理器 创建一个用于管理和切换当前线程使用的具体数据源名称的工具类,比如名为 `DynamicDataSourceContextHolder.java`: ```java public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSourceKey(String key) { System.out.println("Switching DataSource to " + key); CONTEXT_HOLDER.set(key); } public static String getDataSourceKey() { return CONTEXT_HOLDER.get(); } public static void clearDataSourceKey() { CONTEXT_HOLDER.remove(); } } ``` #### 定义抽象路由策略 编写继承自 `AbstractRoutingDataSource` 的类作为实际执行数据源选择逻辑的地方,命名为 `DynamicDataSourceRouter.java` : ```java import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSourceRouter extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } } ``` #### 自动装配并注册所有已知的数据源到容器内 利用 Spring Boot 提供的强大功能自动扫描项目中的 Bean 并将其注入至所需位置。这里需要特别注意的是要确保所有的数据源都被正确加载并且能够被上述路由器识别出来。 ```java @Configuration @MapperScan(basePackages = {"com.example.mapper"}) @EnableTransactionManagement(proxyTargetClass=true) public class DataSourceConfig { @Bean(name="dataSourcePrimary") @ConfigurationProperties(prefix="spring.datasource.primary") public DataSource dataSourcePrimary(){ return DataSourceBuilder.create().build(); } @Bean(name="dataSourceSecondary") @ConfigurationProperties(prefix="spring.datasource.secondary") public DataSource dataSourceSecondary(){ return DataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") public DynamicDataSourceRouter dynamicDataSource(@Qualifier("dataSourcePrimary") DataSource dataSourcePrimary, @Qualifier("dataSourceSecondary") DataSource dataSourceSecondary){ Map<Object,Object> targetDataSources=new HashMap<>(); targetDataSources.put("PRIMARY",dataSourcePrimary); targetDataSources.put("SECONDARY",dataSourceSecondary); DynamicDataSourceRouter dynamicDataSource =new DynamicDataSourceRouter(); dynamicDataSource.setDefaultTargetDataSource(dataSourcePrimary); // 默认指向主库 dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } @Bean public PlatformTransactionManager transactionManager(DynamicDataSourceRouter dynamicDataSource) throws Exception{ return new DataSourceTransactionManager(dynamicDataSource); } } ``` #### 切换数据源的实际操作 当想要改变正在访问的目标数据库时,只需要简单地调用之前提到过的静态方法即可完成切换动作。下面给出一段简单的测试代码片段展示如何做到这一点: ```java @Service public class TestService { @Autowired private JdbcTemplate jdbcTemplate; public List<Map<String, Object>> queryFromPrimaryDb() { DynamicDataSourceContextHolder.setDataSourceKey("PRIMARY"); try { return jdbcTemplate.queryForList("SELECT * FROM table_in_primary LIMIT 5"); } finally { DynamicDataSourceContextHolder.clearDataSourceKey(); } } public int insertIntoSecondaryDb(Map<String, ?> params) { DynamicDataSourceContextHolder.setDataSourceKey("SECONDARY"); try { return jdbcTemplate.update("INSERT INTO table_in_secondary (...) VALUES (...)", params.toArray()); } finally { DynamicDataSourceContextHolder.clearDataSourceKey(); } } } ``` 需要注意的是,在一个类内部调用带有 `@DS` 注解的方法可能会导致 Spring 的代理机制不生效,进而使得数据源切换失败。遇到这种情况可以尝试改用 Spring 的 `ApplicationContext` 进行间接调用来解决问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇宙超级无敌程序媛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值