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

 

posted @ 2022-03-14 14:25  代码红了一大片  阅读(2549)  评论(0编辑  收藏  举报