springboot druid連接池實現多數據源動態切換方式


一、繼承接口AbstractRoutingDataSource(mysql)

主要的starter:

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


<dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.1</version>
        </dependency>


<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

yml中的配置文件:

server:
  port: 8888
spring:
  datasource:
#    url: jdbc:mysql://127.0.0.1:3306/db01?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
#    username: root
#    password: 123456
#    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    datasource1:
      url: jdbc:mysql://127.0.0.1:3306/db01?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver
    datasource2:
      url: jdbc:mysql://127.0.0.1:3306/db02?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver
    datasource3:
      url: jdbc:mysql://127.0.0.1:3306/db03?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
      username: root
      password: 123456
      initial-size: 1
      min-idle: 1
      max-active: 20
      test-on-borrow: true
      driver-class-name: com.mysql.cj.jdbc.Driver


# \u8FD9\u79CD\u65B9\u5F0F\u4E0D\u80FD\u8BBE\u7F6Emybatis plugin
#mybatis:
#  configuration:
#    interceptors:

#      - com.tuling.dynamic.datasource.plugin.DynamicDataSourcePlugin

配置文件:將對應的數據源配置為Bean,自動管理

@Configuration
public class DynamicDataSourceConfig{

    //定義datasource:因為是數據源,所以返回結果為DataSource
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1")
    public DataSource dataSource1(){
        // 底層會自動拿到spring.datasource中的配置, 創建一個DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")
    public DataSource dataSource2(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource3")
    public DataSource dataSource3(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("dataSource1") DataSource dataSource1) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource1);
        return dataSourceTransactionManager;
    }


    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager2(@Qualifier("dataSource2") DataSource dataSource2) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource2);
        return dataSourceTransactionManager;
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager3(@Qualifier("dataSource3") DataSource dataSource3) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource3);
        return dataSourceTransactionManager;
    }


}

繼承(AbstractRoutingDataSource),並進行配置管理

@Component
//這里使用@Primary,才會在程序運行過程中最先使用該類
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {

    public static ThreadLocal<String> name = new ThreadLocal<>();

    @Resource
    DataSource dataSource1;
    @Resource
    DataSource dataSource2;
    @Resource
    DataSource dataSource3;
    /**
     * 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 name.get();
    }

    @Override
    public void afterPropertiesSet() {
        // 為targetDataSources初始化所有數據源
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put("R1", dataSource1);
        targetDataSource.put("R2", dataSource2);
        targetDataSource.put("R3", dataSource3);

        super.setTargetDataSources(targetDataSource);

        //為defaultTargetDataSource 設置默認的數據源
        super.setDefaultTargetDataSource(dataSource1);

        //返回給父類,父類進行數據源的初始化和管理
        super.afterPropertiesSet();
    }
}

最后在選擇自己對應的數據源時,可以根據自己設置的Key值,獲取對應的數據源連接

@GetMapping("/select1")
    public List<UserInfo> queryUserById01(){
        DynamicDataSource.name.set("R1");//這里“R1”就表示當前使用的數據源key為“R1”
        List<UserInfo> userInfos = userService.selectByPrimaryKey();
        return userInfos;
    }

 二、使用@注解加aop動態代理

需要引入的starter

     <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.4.5</version>
        </dependency>


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

 

(1)、定義注解

@Target({ElementType.METHOD,ElementType.TYPE})
//保留級別:
//SOURCE:注釋將被編譯器丟棄
//RUNTIME:注釋將由編譯器記錄在類文件中,並在運行時由 VM 保留,因此可以反射性地讀取它們
//CLASS:注釋將由編譯器記錄在類文件中,但不需要在運行時由 VM 保留。這是默認行為。
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
    String value() default "R1";
}

(2)定義注解的AOP切面

@Component
@Aspect
public class DynamicDataSourceAspect {

    //within(com.zjl.datasources.controller.*)表示在項目中,controller下的所有包使用都會被掃描到,也可以不使用
    //@annotation(ts)表示是注解的切面
    //(JoinPoint point,TargetDataSource ts)表示切入點和掃描到的注解類(ts.value()注解中的value對象)
    //@Before("within(com.zjl.datasources.controller.*) && @annotation(ts)")
    @Before("@annotation(ts)")
    public void  before(JoinPoint point, TargetDataSource ts){
        String value = ts.value();
        DynamicDataSource.name.set(value);
        System.out.println(value);
    }
}
/**
*注意在主類中引入自動掃描配置容器和允許切面生成AOp代理
*/
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@MapperScan("com.zjl.datasources.mapper")
@ComponentScan(basePackages = {"com.zjl.datasources.*"})
@EnableAspectJAutoProxy(exposeProxy=true)
public class DatasourcesApplication {

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

}
 
        

(3)多數據源Bean配置(配置類)

@Configuration
public class DynamicDataSourceConfig{

    //定義datasource
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1")
    public DataSource dataSource1(){
        // 底層會自動拿到spring.datasource中的配置, 創建一個DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")
    public DataSource dataSource2(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource3")
    public DataSource dataSource3(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("dataSource1") DataSource dataSource1) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource1);
        return dataSourceTransactionManager;
    }


    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager2(@Qualifier("dataSource2") DataSource dataSource2) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource2);
        return dataSourceTransactionManager;
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager3(@Qualifier("dataSource3") DataSource dataSource3) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource3);
        return dataSourceTransactionManager;
    }

    //使用mybatis的bean
    //@Bean
    //public Interceptor dynamicDataSourcePlugin(){
    //    return new DynamicDataSourcePlugin();
    //}


}

(4)動態多數據源配置

@Component
//這里使用@Primary,才會在程序運行過程中最先使用該類
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {

    public static ThreadLocal<String> name = new ThreadLocal<>();

    @Resource
    DataSource dataSource1;
    @Resource
    DataSource dataSource2;
    @Resource
    DataSource dataSource3;
    /**
     * 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 name.get();
    }

    @Override
    public void afterPropertiesSet() {
        // 為targetDataSources初始化所有數據源
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put("R1", dataSource1);
        targetDataSource.put("R2", dataSource2);
        targetDataSource.put("R3", dataSource3);

        super.setTargetDataSources(targetDataSource);

        //為defaultTargetDataSource 設置默認的數據源
        super.setDefaultTargetDataSource(dataSource1);

        //返回給父類,父類進行數據源的初始化和管理
        super.afterPropertiesSet();
    }
}

使用注解指定對應的數據源

    @GetMapping("/select1")
    @TargetDataSource("R3")
    public List<UserInfo> queryUserById01(){
        //DynamicDataSource.name.set("R1");
        List<UserInfo> userInfos = userService.selectByPrimaryKey();
        return userInfos;
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM