zoukankan      html  css  js  c++  java
  • springboot多数据源

    springboot多数据源

    pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
       <parent>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-parent</artifactId>
           <version>2.3.5.RELEASE</version>
           <relativePath/> <!-- lookup parent from repository -->
       </parent>
       <groupId>com.dtg</groupId>
       <artifactId>muldatasource</artifactId>
       <version>0.0.1-SNAPSHOT</version>
       <name>muldatasource</name>
       <description>Demo project for Spring Boot</description>

       <properties>
           <java.version>1.8</java.version>
       </properties>

       <dependencies>
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-data-jpa</artifactId>
           </dependency>
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-jdbc</artifactId>
           </dependency>
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-web</artifactId>
           </dependency>

           <dependency>
               <groupId>mysql</groupId>
               <artifactId>mysql-connector-java</artifactId>
               <version>5.1.25</version>
           </dependency>


           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-test</artifactId>
               <scope>test</scope>
               <exclusions>
                   <exclusion>
                       <groupId>org.junit.vintage</groupId>
                       <artifactId>junit-vintage-engine</artifactId>
                   </exclusion>
               </exclusions>
           </dependency>
       </dependencies>

       <build>
           <plugins>
               <plugin>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-maven-plugin</artifactId>
               </plugin>
           </plugins>
       </build>

    </project>

     

    application.yml配置文件

    增加读和写两个数据源

    spring:
    datasource:
      write:
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      read:
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://127.0.0.1:3308/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

    ##

     

     

     

     

    定义数据源类型

    package com.dtg.muldatasource.constants;

    public enum DataSourceType {
       WRITE,
       READ
    }

    定义数据源holder

    holder维护了每个线程所需要的数据源类型,并对外暴露了设置数据源变量的方法。

    package com.dtg.muldatasource.config;

    public class DynamicDataSourceContextHolder {

       /**
        * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
        * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
        */
       private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

       /**
        * 设置数据源变量
        * @param dataSourceType
        */
       public static void setDataSourceType(String dataSourceType){
           System.out.println("切换到"+dataSourceType+"数据源");
           CONTEXT_HOLDER.set(dataSourceType);
      }

       /**
        * 获取数据源变量
        * @return
        */
       public static String getDataSourceType(){
           return CONTEXT_HOLDER.get();
      }

       /**
        * 清空数据源变量
        */
       public static void clearDataSourceType(){
           CONTEXT_HOLDER.remove();
      }
    }

    动态数据源类

    通常用 springboot 时都是使用它的默认配置,只需要在配置文件中定义好连接属性就行了,但是现在我们需要自己来配置了,spring 是支持多数据源的,多个 datasource 放在一个 HashMapTargetDataSource中,通过dertermineCurrentLookupKey获取 key 来觉定要使用哪个数据源。因此我们的目标就很明确了,建立多个 datasource 放到 TargetDataSource 中,同时重写 dertermineCurrentLookupKey 方法来决定使用哪个 key。

    这里的dertermineCurrentLookupKey 是查询DynamicDataSourceContextHolder中的数据源类型。

    package com.dtg.muldatasource.config;

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

    import javax.sql.DataSource;
    import java.util.Map;

    public class DynamicDataSource extends AbstractRoutingDataSource {

       /**
        *
        * @param defaultTargetDataSource 默认的数据源
        * @param targetDataSources 支持的所有数据源
        */
       public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
           super.setDefaultTargetDataSource(defaultTargetDataSource);
           super.setTargetDataSources(targetDataSources);
           // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
           super.afterPropertiesSet();
      }

       /**
        * 重写该方法,根据Key获取数据源的信息
        *
        * @return
        */
       @Override
       protected Object determineCurrentLookupKey() {
           return DynamicDataSourceContextHolder.getDataSourceType();
      }
    }

     

    数据源配置类DataSourceConfig

    package com.dtg.muldatasource.config;

    import com.dtg.muldatasource.constants.DataSourceType;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;

    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;

    @Configuration
    public class DataSourceConfig {
       @Bean
       @ConfigurationProperties("spring.datasource.write")
       public DataSource writeDataSource() {
           return DataSourceBuilder.create().build();
      }

       @Bean
       @ConfigurationProperties("spring.datasource.read")
       public DataSource readDataSource() {
           return DataSourceBuilder.create().build();
      }

       @Bean(name = "dynamicDataSource")
       @Primary
       public DynamicDataSource dataSource(DataSource writeDataSource, DataSource readDataSource) {
           Map<Object, Object> targetDataSources = new HashMap<>();
           targetDataSources.put(DataSourceType.WRITE.name(), writeDataSource);
           targetDataSources.put(DataSourceType.READ.name(), readDataSource);
           return new DynamicDataSource(writeDataSource, targetDataSources);
      }

    }

    数据源注解

    package com.dtg.muldatasource.config;

    import com.dtg.muldatasource.constants.DataSourceType;

    import java.lang.annotation.*;

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyDataSource {
       /**
        * 切换数据源名称
        */
       DataSourceType value() default DataSourceType.WRITE;
    }

    数据源切面

    package com.dtg.muldatasource.aspect;

    import com.dtg.muldatasource.config.DynamicDataSourceContextHolder;
    import com.dtg.muldatasource.config.MyDataSource;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;

    import java.lang.reflect.Method;

    @Aspect
    @Order(1)
    @Component
    public class DataSourceAspect {

       @Pointcut("@annotation(com.dtg.muldatasource.config.MyDataSource)")
       public void dsPointCut() { }


       //@Before:在切入点方法执行之前执行该方法。
       @Before("dsPointCut()")
       public void  before(JoinPoint joinPoint){
           System.out.println("dsPointCut @before....");
      }

       // @AfterReturning:在切入点方法执行并有返回值才执行该方法。
       @AfterReturning("dsPointCut()")
       public void  AfterReturning(JoinPoint joinPoint){
           System.out.println("dsPointCut @AfterReturning....");
      }

       // @After:在执行切入点方法之后执行该方法。
       @After("dsPointCut()")
       public void  After(JoinPoint joinPoint){
           System.out.println("dsPointCut @After....");
      }

       @Around("dsPointCut()")
       public Object around(ProceedingJoinPoint point) throws Throwable {
           MethodSignature signature = (MethodSignature) point.getSignature();
           Method method = signature.getMethod();
           MyDataSource dataSource = method.getAnnotation(MyDataSource.class); //方法上获取 数据源的注解
           if (dataSource != null) {
               DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); //获取注解上的value
          }

           System.out.println("dsPointCut @Around start....");

           Object rs;
           try {
               rs =   point.proceed();
               return rs;
          } finally {
               // 销毁数据源 在执行方法之后
               DynamicDataSourceContextHolder.clearDataSourceType();
          }


      }
    }

     

    测试类:

     

    package com.dtg.muldatasource.controller;

    import com.dtg.muldatasource.config.MyDataSource;
    import com.dtg.muldatasource.constants.DataSourceType;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;
    import java.util.Map;


    @RestController
    public class EmpController {

       @Autowired
       JdbcTemplate jdbcTemplate;

       @GetMapping("/write")
       @MyDataSource(value = DataSourceType.WRITE)
       public List<Map<String, Object>> local(){
           List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from customer");
           return maps;
      }
       @GetMapping("/read")
       @MyDataSource(value = DataSourceType.READ)
       public List<Map<String, Object>> remote(){
           List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from customer");
           return maps;
      }

    }

    启动类:

    package com.dtg.muldatasource;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    public class MuldatasourceApplication {

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

    }

     

  • 相关阅读:
    QT代理Delegates使用实例(三种代理控件)
    delphi中的各种文件类型介绍
    猎豹傅盛:老大老二打仗老三就没了 不要边缘化
    老调重弹,大学有没有意义(没有利用好时间,读什么都没有用)
    应聘linux/ARM嵌入式开发岗位
    气死人不偿命,Q_OBJECT导致的C++报错,而且还看不明白(#ifdef没控制好,导致什么都不认识了)
    坚果云创业团队访谈:我们 DIY 云存储(不要过度关注竞争对手,尤其当我们还是小公司的时候)
    中国版dropbox“坚果云”和它背后的团队故事(大的优势就在于他为用户提供了设定多个文件夹的权利)
    一个灵巧的Delphi多播实事件现方案
    雷军的B面:那些赔到血本无归的失败投资案例
  • 原文地址:https://www.cnblogs.com/datangguott/p/13950875.html
Copyright © 2011-2022 走看看