最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot項(xiàng)目實(shí)現(xiàn)Mysql多數(shù)據(jù)源切換的完整實(shí)例

 更新時(shí)間:2020年11月06日 09:39:00   作者:迪迦Monster  
這篇文章主要給大家介紹了關(guān)于Springboot項(xiàng)目實(shí)現(xiàn)Mysql多數(shù)據(jù)源切換的完整實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、分析AbstractRoutingDataSource抽象類源碼

關(guān)注import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource以下變量

@Nullable
private Map<Object, Object> targetDataSources; // 目標(biāo)數(shù)據(jù)源
@Nullable
private Object defaultTargetDataSource; // 默認(rèn)目標(biāo)數(shù)據(jù)源
@Nullable
private Map<Object, DataSource> resolvedDataSources; // 解析的數(shù)據(jù)源
@Nullable
private DataSource resolvedDefaultDataSource; // 解析的默認(rèn)數(shù)據(jù)源

這兩組變量是相互對(duì)應(yīng)的,在熟悉多實(shí)例數(shù)據(jù)源切換代碼的不難發(fā)現(xiàn),當(dāng)有多個(gè)數(shù)據(jù)源的時(shí)候,一定要指定一個(gè)作為默認(rèn)的數(shù)據(jù)源;

在這里也同理,當(dāng)同時(shí)初始化多個(gè)數(shù)據(jù)源的時(shí)候,需要顯式的調(diào)用setDefaultTargetDataSource方法指定一個(gè)作為默認(rèn)數(shù)據(jù)源;

我們需要關(guān)注的是Map<Object, Object> targetDataSources和Map<Object, DataSource> resolvedDataSources;

targetDataSources是暴露給外部程序用來(lái)賦值的,而resolvedDataSources是程序內(nèi)部執(zhí)行時(shí)的依據(jù),因此會(huì)有一個(gè)賦值的操作;

根據(jù)這段源碼可以看出,每次執(zhí)行時(shí),都會(huì)遍歷targetDataSources內(nèi)的所有元素并賦值給resolvedDataSources;這樣如果我們?cè)谕獠砍绦蛐略鲆粋€(gè)新的數(shù)據(jù)源,都會(huì)添加到內(nèi)部使用,從而實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)加載。
繼承該抽象類的時(shí)候,必須實(shí)現(xiàn)一個(gè)抽象方法:protected abstract Object determineCurrentLookupKey(),該方法用于指定到底需要使用哪一個(gè)數(shù)據(jù)源。

二、實(shí)現(xiàn)多數(shù)據(jù)源切換和動(dòng)態(tài)數(shù)據(jù)源加載

A - 配置文件信息

application.yml文件

server:
 port: 18080
spring:
 datasource:
 type: com.alibaba.druid.pool.DruidDataSource
 druid:
  # 主數(shù)據(jù)源
  master-db:
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://192.168.223.129:13306/test_master_db?characterEncoding=utf-8
  username: josen
  password: josen
  # 從數(shù)據(jù)源
  slave-db:
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://192.168.223.129:13306/test_slave_db?characterEncoding=utf-8
  username: josen
  password: josen
mybatis:
 mapper-locations: classpath:mapper/*.xml
logging:
 path: ./logs/mydemo20201105.log
 level:
 com.josen.mydemo20201105: debug

maven 依賴

<dependencies>
 <!-- AOP -->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
 <!-- druid -->
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.10</version>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.3</version>
 </dependency>
 <dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
 </dependency>
 <dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
 </dependency>
</dependencies>

B - 編碼實(shí)現(xiàn)

1、創(chuàng)建一個(gè)DynamicDataSource類,繼承AbstractRoutingDataSource抽象類,實(shí)現(xiàn)determineCurrentLookupKey方法,通過該方法指定當(dāng)前使用哪個(gè)數(shù)據(jù)源;

2、 在DynamicDataSource類中通過ThreadLocal維護(hù)一個(gè)全局?jǐn)?shù)據(jù)源名稱,后續(xù)通過修改該名稱實(shí)現(xiàn)動(dòng)態(tài)切換;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;

/**
 * @ClassName DynamicDataSource
 * @Description 設(shè)置動(dòng)態(tài)數(shù)據(jù)源
 * @Author Josen
 * @Date 2020/11/5 14:28
 **/
public class DynamicDataSource extends AbstractRoutingDataSource {
 // 通過ThreadLocal維護(hù)一個(gè)全局唯一的map來(lái)實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換
 private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

 public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
  super.setDefaultTargetDataSource(defaultTargetDataSource);
  super.setTargetDataSources(targetDataSources);
  super.afterPropertiesSet();
 }

 /**
  * 指定使用哪一個(gè)數(shù)據(jù)源
  */
 @Override
 protected Object determineCurrentLookupKey() {
  return getDataSource();
 }

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

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

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

3、創(chuàng)建DynamicDataSourceConfig類,引入application.yaml配置的多個(gè)數(shù)據(jù)源信息,構(gòu)建多個(gè)數(shù)據(jù)源;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

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

/**
 * @ClassName DynamicDataSourceConfig
 * @Description 引入動(dòng)態(tài)數(shù)據(jù)源,構(gòu)建數(shù)據(jù)源
 * @Author Josen
 * @Date 2020/11/5 14:23
 **/
@Configuration
@Component
public class DynamicDataSourceConfig {
 /**
  * 讀取application配置,構(gòu)建master-db數(shù)據(jù)源
  */
 @Bean
 @ConfigurationProperties("spring.datasource.druid.master-db")
 public DataSource myMasterDataSource(){
  return DruidDataSourceBuilder.create().build();
 }

 /**
  * 讀取application配置,構(gòu)建slave-db數(shù)據(jù)源
  */
 @Bean
 @ConfigurationProperties("spring.datasource.druid.slave-db")
 public DataSource mySlaveDataSource(){
  return DruidDataSourceBuilder.create().build();
 }
 /**
  * 讀取application配置,創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源
  */
 @Bean
 @Primary
 public DynamicDataSource dataSource(DataSource myMasterDataSource, DataSource mySlaveDataSource) {
  Map<Object, Object> targetDataSources = new HashMap<>();
  targetDataSources.put("master-db",myMasterDataSource);
  targetDataSources.put("slave-db", mySlaveDataSource);
  // myMasterDataSource=默認(rèn)數(shù)據(jù)源
  // targetDataSources=目標(biāo)數(shù)據(jù)源(多個(gè))
  return new DynamicDataSource(myMasterDataSource, targetDataSources);
 }
}

4、 創(chuàng)建MyDataSource自定義注解,后續(xù)AOP通過該注解作為切入點(diǎn),通過獲取使用該注解存入不同的值動(dòng)態(tài)切換指定的數(shù)據(jù)源;

import java.lang.annotation.*;
/**
 * 自定義數(shù)據(jù)源注解
 * 在需要切換數(shù)據(jù)源的Service層方法上添加此注解,指定數(shù)據(jù)源名稱
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyDataSource {
 String name() default "";
}

5、 創(chuàng)建DataSourceAspectAOP切面類,以MyDataSource注解為切入點(diǎn)作拓展。在執(zhí)行被@MyDataSource注解的方法時(shí),獲取該注解傳入的name,并切換到指定數(shù)據(jù)源執(zhí)行,執(zhí)行完成后切換回默認(rèn)數(shù)據(jù)源;

import com.josen.mydemo20201105.annotation.MyDataSource;
import com.josen.mydemo20201105.datasource.DynamicDataSource;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.reflect.Method;

/**
 * @ClassName DataSourceAspect
 * @Description Aop切面類配置
 * @Author Josen
 * @Date 2020/11/5 14:35
 **/
@Aspect
@Component
@Slf4j
public class DataSourceAspect {
 private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

 /**
  * 設(shè)置切入點(diǎn)
  * 只有調(diào)用@MyDataSource注解的方法才會(huì)觸發(fā)around
  */
 @Pointcut("@annotation(com.josen.mydemo20201105.annotation.MyDataSource)")
 public void dataSourcePointCut() {
 }

 /**
  * 截取使用MyDataSource注解的方法,切換指定數(shù)據(jù)源
  * 環(huán)繞切面:是(前置&后置&返回&異常)通知的結(jié)合體,更像是動(dòng)態(tài)代理的整個(gè)過程
  * @param point
  */
 @Around("dataSourcePointCut()")
 public Object around(ProceedingJoinPoint point) throws Throwable {
  MethodSignature signature = (MethodSignature) point.getSignature();
  Method method = signature.getMethod();
  logger.info("execute DataSourceAspect around=========>"+method.getName());
  // 1. 獲取自定義注解MyDataSource,查看是否配置指定數(shù)據(jù)源名稱
  MyDataSource dataSource = method.getAnnotation(MyDataSource.class);
  if(dataSource == null){
   // 1.1 使用默認(rèn)數(shù)據(jù)源
   DynamicDataSource.setDataSource("master-db");
  }else {
   // 1.2 使用指定名稱數(shù)據(jù)源
   DynamicDataSource.setDataSource(dataSource.name());
   logger.info("使用指定名稱數(shù)據(jù)源=========>"+dataSource.name());
  }
  try {
   return point.proceed();
  } finally {
   // 后置處理 - 恢復(fù)默認(rèn)數(shù)據(jù)源
   DynamicDataSource.clearDataSource();
  }
 }
}

6、 配置啟動(dòng)類

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) // 不加載默認(rèn)數(shù)據(jù)源配置
@MapperScan(basePackages = "com.josen.mydemo.mapper")
public class MydemoApplication {
 public static void main(String[] args) {
  SpringApplication.run(MydemoApplication.class, args);
 }
}

7、 到這里基本上已經(jīng)完成,剩下的就是測(cè)試是否正確切換數(shù)據(jù)源了;

Mapper層

@Repository
public interface PermissionMapper {
 List<Permission> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.josen.mydemo.mapper.PermissionMapper">
 <select id="findAll" resultType="com.josen.mydemo20201105.pojo.Permission">
  select * from t_permission;
 </select>
</mapper>

Service層

@Service
public class PermissionService {
 @Autowired
 private PermissionMapper permissionMapper;
	// 切換從庫(kù)數(shù)據(jù)源
 @MyDataSource(name = "slave-db")
 public List<Permission> findSlaveAll(){
  return permissionMapper.findAll();
 }
	// 默認(rèn)數(shù)據(jù)源
 public List<Permission> findAll(){
  return permissionMapper.findAll();
 }
}

Controller層

@RestController
@RequestMapping("/permission")
public class PermissionController {
 @Autowired
 private PermissionService permissionService;

 // 測(cè)試獲取默認(rèn)master-db數(shù)據(jù)
 @GetMapping("/master")
 public List<Permission> handlerFindAll() {
  List<Permission> list = permissionService.findAll();
  return list;
 }

 // 測(cè)試獲取指定slave-db數(shù)據(jù)
 @GetMapping("/slave")
 public List<Permission> handlerFindAll2() {
  List<Permission> list = permissionService.findSlaveAll();
  return list;
 }
}

C - 測(cè)試數(shù)據(jù)源切換

Mysql數(shù)據(jù)

接口返回?cái)?shù)據(jù)

Demo源碼地址:https://gitee.com/taco-gigigi/multiple-data-sources

總結(jié)

到此這篇關(guān)于Springboot項(xiàng)目實(shí)現(xiàn)Mysql多數(shù)據(jù)源切換的文章就介紹到這了,更多相關(guān)Springboot項(xiàng)目Mysql多數(shù)據(jù)源切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JPA like 模糊查詢 語(yǔ)法格式解析

    JPA like 模糊查詢 語(yǔ)法格式解析

    這篇文章主要介紹了JPA like 模糊查詢 語(yǔ)法格式解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • springboot打war包的全過程記錄

    springboot打war包的全過程記錄

    其實(shí)一般使用springboot使用打成jar包比較省事的,但也有很多童鞋是習(xí)慣使用war包的,下面這篇文章主要給大家介紹了關(guān)于springboot打war包的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • springboot-啟動(dòng)bean沖突的解決

    springboot-啟動(dòng)bean沖突的解決

    這篇文章主要介紹了springboot-啟動(dòng)bean沖突的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java 中迭代器的使用方法詳解

    java 中迭代器的使用方法詳解

    這篇文章主要介紹了java 中迭代器的使用方法詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • Spring4整合Hibernate5詳細(xì)步驟

    Spring4整合Hibernate5詳細(xì)步驟

    本篇文章主要介紹了Spring4整合Hibernate5詳細(xì)步驟,具有一定的參考價(jià)值,有興趣的同學(xué)可以了解一下
    2017-04-04
  • SpringBoot整合阿里云短信服務(wù)的案例代碼

    SpringBoot整合阿里云短信服務(wù)的案例代碼

    這篇文章主要介紹了SpringBoot整合阿里云短信服務(wù)的案例代碼,在Spring Boot項(xiàng)目的pom.xml文件中添加阿里云短信服務(wù)SDK的依賴,本文通過示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2024-06-06
  • 詳解使用Spring?Data?repository進(jìn)行數(shù)據(jù)層的訪問問題

    詳解使用Spring?Data?repository進(jìn)行數(shù)據(jù)層的訪問問題

    這篇文章主要介紹了使用Spring?Data?repository進(jìn)行數(shù)據(jù)層的訪問,抽象出Spring Data repository是因?yàn)樵陂_發(fā)過程中,常常會(huì)為了實(shí)現(xiàn)不同持久化存儲(chǔ)的數(shù)據(jù)訪問層而寫大量的大同小異的代碼,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-06-06
  • Spring超出最大會(huì)話數(shù)(Max?sessions?limit?reached:?10000)

    Spring超出最大會(huì)話數(shù)(Max?sessions?limit?reached:?10000)

    在Spring系統(tǒng)中遇到的Maxsessionslimitreached:10000錯(cuò)誤,該錯(cuò)誤由于會(huì)話數(shù)超過默認(rèn)限制10000而觸發(fā),下面就來(lái)介紹一下解決方法,感興趣的可以了解一下
    2024-12-12
  • Spring?Boot?Admin?添加報(bào)警提醒和登錄驗(yàn)證功能的具體實(shí)現(xiàn)

    Spring?Boot?Admin?添加報(bào)警提醒和登錄驗(yàn)證功能的具體實(shí)現(xiàn)

    報(bào)警提醒功能是基于郵箱實(shí)現(xiàn)的,當(dāng)然也可以使用其他的提醒功能,如釘釘或飛書機(jī)器人提醒也是可以的,但郵箱報(bào)警功能的實(shí)現(xiàn)成本最低,所以本文我們就來(lái)看郵箱的報(bào)警提醒功能的具體實(shí)現(xiàn)
    2022-01-01
  • Java使用5個(gè)線程計(jì)算數(shù)組之和

    Java使用5個(gè)線程計(jì)算數(shù)組之和

    本文主要介紹了Java使用5個(gè)線程計(jì)算數(shù)組之和,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05

最新評(píng)論

新宾| 莎车县| 芜湖县| 平湖市| 石柱| 仙居县| 溆浦县| 宁安市| 平凉市| 乐山市| 武汉市| 花莲市| 大关县| 辽宁省| 石狮市| 博湖县| 博湖县| 太仓市| 长沙市| 友谊县| 彭水| 陈巴尔虎旗| 榆树市| 香河县| 板桥市| 阳山县| 北海市| 静安区| 神农架林区| 安顺市| 德化县| 邹城市| 延吉市| 疏附县| 高邑县| 云安县| 金坛市| 浠水县| 博爱县| 长治市| 合江县|