1. 程式人生 > 程式設計 >Springboot整合多資料來源程式碼示例詳解

Springboot整合多資料來源程式碼示例詳解

最近有個老專案想逐步將新業務的資料放到新的資料庫,以前的業務還得連線以前的資料庫,於是需要整合多資料來源 。

多資料來源實際上是繼承了AbstractRoutingDataSource類,這個類最終實現了DataSource介面,DataSource裡只有一個getConnection方法,資料庫每次訪問的時候都要先通過這個方法獲取連線,所有多資料來源就是每次訪問資料庫之前動態的改變資料來源。

在請求前改變資料來源當然需要用到SpringAOP,自定義註解操作

專案結構

Springboot整合多資料來源程式碼示例詳解

下面上程式碼:

首先是依賴:

<!--資料庫連線-->
    <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>
    <!--sqlserver-->
    <dependency>
      <groupId>com.microsoft.sqlserver</groupId>
      <artifactId>mssql-jdbc</artifactId>
      <scope>runtime</scope>
    </dependency>
    <!--mybatis-plus-->
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.1.2</version>
    </dependency>
    <!--資料庫連線池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.8</version>
    </dependency>      <!--AOP-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

yml配置資料來源

server:
 port: 8888

spring:
 jackson:
  time-zone: GMT+8
  date-format: yyyy-MM-dd HH:mm:ss
 datasource:
  druid:
   first:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    jdbc-url: jdbc:mysql://rm-uf6265pj340sc9447oo.mysql.rds.54565.com:3306/dm?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=utf-8
    username: username
    password: password
   second:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
    jdbc-url: jdbc:sqlserver://39.104.203.222:1433;DatabaseName=TestTLcom
    username: root
    password: 123456

mybatis-plus:
 mapper-locations: classpath*:/mapper/*Mapper.xml
 type-aliases-package: com.zdyl.dynamicdatasourcedemo.entity
 global-config:
  #主鍵型別 0:"資料庫ID自增",1:"使用者輸入ID",2:"全域性唯一ID (數字型別唯一ID)",3:"全域性唯一ID UUID";
  id-type: 3
  #欄位策略 0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷"
  field-strategy: 2
  #駝峰下劃線轉換
  db-column-underline: true
  #重新整理mapper 除錯神器
  refresh-mapper: true
  #資料庫大寫下劃線轉換
  #capital-mode: true
  #序列介面實現類配置
  #key-generator: com.baomidou.springboot.xxx
  #邏輯刪除配置
  #logic-delete-value: 0
  #logic-not-delete-value: 1
  #自定義填充策略介面實現
  #meta-object-handler: com.baomidou.springboot.xxx
  #自定義SQL注入器
  #sql-injector: com.baomidou.springboot.xxx
 configuration:
  map-underscore-to-camel-case: true
  cache-enabled: false

定義資料庫名稱

/**
 * 資料庫名稱
 */
public interface DataSourceNames {

  String FIRST = "first";
  String SECOND = "second";
}

動態資料來源

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

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

/**
 * 動態資料來源
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

  private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

  public DynamicDataSource(DataSource defaultTargetDataSource,Map<String,DataSource> targetDataSources) {

    super.setDefaultTargetDataSource(defaultTargetDataSource);
    super.setTargetDataSources(new HashMap<>(targetDataSources));
    super.afterPropertiesSet();
  }

  @Override
  protected Object determineCurrentLookupKey() {
    return getDataSource();
  }

  public static String getDataSource() {
    return contextHolder.get();
  }

  public static void setDataSource(String dataSource) {
    contextHolder.set(dataSource);
  }

  public static void clearDataSource() {
    contextHolder.remove();
  }
}

配置多資料來源

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DataSourceNames;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DynamicDataSource;
import org.mybatis.spring.annotation.MapperScan;
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
@MapperScan("com.zdyl.dynamicdatasourcedemo.**.mapper*")
public class MybatisPluConfig {

  /**
   * 資料來源配置
   * @return
   */
  @Bean
  @ConfigurationProperties(prefix="spring.datasource.druid.first")
  public DataSource firstDataSource() {
    return DataSourceBuilder.create().build();
  }

  @Bean
  @ConfigurationProperties(prefix="spring.datasource.druid.second")
  public DataSource secondDataSource() {
    return DataSourceBuilder.create().build();
  }

  @Bean
  @Primary
  public DynamicDataSource dataSource(DataSource firstDataSource,DataSource secondDataSource){
    Map<String,DataSource> targetDataSources = new HashMap<>();
    targetDataSources.put(DataSourceNames.FIRST,firstDataSource);
    targetDataSources.put(DataSourceNames.SECOND,secondDataSource);
    return new DynamicDataSource(firstDataSource,targetDataSources);
  }

  /**
   * mybatis-plus分頁外掛<br>
   * 文件:http://mp.baomidou.com<br>
   */
  @Bean
  public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
  }

}

下面就是自定義註解

import java.lang.annotation.*;

/**
 * 多資料來源註解
 * AOP攔截此註解更換資料來源
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurDataSource {

  String name() default "";
}

AOP

import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DataSourceNames;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.DynamicDataSource;
import com.zdyl.dynamicdatasourcedemo.dynamicdatasource.annotation.CurDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


/**
 * 多資料來源,切面處理類
 * AOP攔截多資料來源註解 @CurDataSource 註解更換資料來源
 */

@Slf4j
@Aspect
@Component
public class DataSourceAspect implements Ordered {


  /**
   * 切點
   */
  @Pointcut("@annotation(com.zdyl.dynamicdatasourcedemo.dynamicdatasource.annotation.CurDataSource)")
  public void dataSourcePointCut() {

  }

  @Around("dataSourcePointCut()")
  public Object around(ProceedingJoinPoint point) throws Throwable {
    MethodSignature signature = (MethodSignature) point.getSignature();
    Method method = signature.getMethod();
    CurDataSource curDataSource = method.getAnnotation(CurDataSource.class);
    if (curDataSource == null) {
      DynamicDataSource.setDataSource(DataSourceNames.FIRST);
      log.info("set datasource is " + DataSourceNames.FIRST);
    } else {
      DynamicDataSource.setDataSource(curDataSource.name());
      log.info("set datasource is " + curDataSource.name());
    }
    try {
      return point.proceed();
    } finally {
      DynamicDataSource.clearDataSource();
      log.info("clean datasource");
    }
  }

  @Override
  public int getOrder() {
    return 1;
  }
}

最後主啟動了去掉資料來源自動載入

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

最後我們來跑起來請求一下,測試一下是否正確

@RestController
public class CfgDeviceController {
  @Resource
  CfgDeviceService cfgDeviceService;
  @Resource
  CfgChargeStartInfoService cfgChargeStartInfoService;
  
  @CurDataSource(name = DataSourceNames.FIRST)
  @GetMapping("/test")
  public void getUser() {
    CfgDevice byId = cfgDeviceService.getById(19);
    System.out.println(byId.toString());
  }

  @CurDataSource(name = DataSourceNames.SECOND)
  @GetMapping("/test1")
  public void getUser1() {
    CfgChargeStartInfo byId = cfgChargeStartInfoService.getById(1);
    System.out.println(byId.toString());
  }
}

Springboot整合多資料來源程式碼示例詳解

**如果不加註解,使用預設資料來源

至此就整合完了

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。