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

}

 

原文地址:https://www.cnblogs.com/datangguott/p/13950875.html