欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例

 更新時(shí)間:2024年01月04日 09:25:24   作者:LeoToJavaer  
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例,文中通過(guò)示例代碼和圖文展示介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧

1.前言

大家好,今天給大家?guī)?lái)一篇關(guān)于SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例。好了,話不多說(shuō)讓我們開(kāi)始吧.

2.概述

在實(shí)際開(kāi)發(fā)中,我們往往面臨一個(gè)應(yīng)用需要訪問(wèn)多個(gè)數(shù)據(jù)庫(kù)的情況。例如下面兩種場(chǎng)景。

  • 業(yè)務(wù)復(fù)雜: 數(shù)據(jù)分布在不同的數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)拆了,應(yīng)用沒(méi)拆,一個(gè)公司有多個(gè)子項(xiàng)目,各用各的數(shù)據(jù)庫(kù)。
  • 寫分離: 為了解決數(shù)據(jù)庫(kù)的讀性能瓶頸(讀比寫性能更高,寫鎖會(huì)影響讀阻塞,從而影響讀的性能)
  • 很多數(shù)據(jù)庫(kù)擁有主從架構(gòu),也就是說(shuō),一臺(tái) 主數(shù)據(jù)庫(kù)服務(wù)器,是對(duì)外提供增刪改查業(yè)務(wù)的生產(chǎn)服務(wù)器;
  • 另一臺(tái)從數(shù)據(jù)庫(kù)服務(wù)器,主要進(jìn)行讀的操作。
  • 讀寫分離:解決高并發(fā)下讀寫受影響。數(shù)據(jù)更新在主庫(kù)上進(jìn)行,主庫(kù)將數(shù)據(jù)變更信息同步給從庫(kù)。在查詢時(shí),在從庫(kù)上進(jìn)行,從而分擔(dān)主庫(kù)的壓力。

我們可以在代碼層面解決這種動(dòng)態(tài)數(shù)據(jù)源切換的問(wèn)題,而不需要使用 mycat、shardingJDBC 等其他中間件。本文將主要以自定義注解 + 繼承 AbstractRoutingDataSource 實(shí)現(xiàn)讀寫分離。

3.如何實(shí)現(xiàn)多數(shù)據(jù)源

SpringBoot 項(xiàng)目中實(shí)現(xiàn)讀寫分離通常需要以下幾步:

  • 配置數(shù)據(jù)源:你需要為讀操作和寫操作分別配置一個(gè)數(shù)據(jù)源。
  • 創(chuàng)建數(shù)據(jù)源路由邏輯:這通常通過(guò)擴(kuò)展 Spring 的 AbstractRoutingDataSource 來(lái)實(shí)現(xiàn)。它允許你根據(jù)一定的邏輯來(lái)決定使用哪個(gè)數(shù)據(jù)源(讀或?qū)懀?/li>
  • 配置事務(wù)管理器:這使得你能夠在使用不同數(shù)據(jù)源時(shí)保持事務(wù)的一致性。
  • 服務(wù)層或DAO層設(shè)計(jì):確保在執(zhí)行讀操作時(shí)使用讀數(shù)據(jù)源,在執(zhí)行寫操作時(shí)使用寫數(shù)據(jù)源。
  • 自定義切面,在切面中解析 @DataSource 注解。當(dāng)一個(gè)方法或者類上面,有 @DataSource 注解的時(shí)候,將 @DataSource 注解所標(biāo)記的數(shù)據(jù)源列出來(lái)存入到 ThreadLocal 中。

注意:這里使用ThreadLocal的原因是為了保證我們的線程安全。

4.案例實(shí)現(xiàn)

接下來(lái)我們就按照以上步驟進(jìn)行編碼實(shí)現(xiàn)。

4.1 創(chuàng)建新模塊

首先我們創(chuàng)建一個(gè)新的模塊命名為:springboot-dynamic-source

image-20240102183810902

1.導(dǎo)入依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <!--mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
    </dependencies>

2.創(chuàng)建yml配置文件

server:
  port: 8007


spring:
  application:
    name:  dynamic-source
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    ds:
      # 主庫(kù)數(shù)據(jù)源
      master:
        url: jdbc:mysql://localhost:3307/test01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: root
      # 從庫(kù)數(shù)據(jù)源
      slave:
        url: jdbc:mysql://localhost:3307/test02?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: root
    # 初始連接數(shù)
    initialSize: 5
    # 最小連接池?cái)?shù)量
    minIdle: 10
    # 最大連接池?cái)?shù)量
    maxActive: 20
    # 配置獲取連接等待超時(shí)的時(shí)間
    maxWait: 60000
    # 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
    minEvictableIdleTimeMillis: 300000
    # 配置一個(gè)連接在池中最大生存的時(shí)間,單位是毫秒
    maxEvictableIdleTimeMillis: 900000
    # 配置檢測(cè)連接是否有效
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    webStatFilter:
      enabled: true
    statViewServlet:
      enabled: true
      # 設(shè)置白名單,不填則允許所有訪問(wèn)
      allow:
      url-pattern: /druid/*
      # 控制臺(tái)管理用戶名和密碼
      login-username: admin
      login-password: 123456
    filter:
      stat:
        enabled: true
        # 慢SQL記錄
        log-slow-sql: true
        slow-sql-millis: 1000
        merge-sql: true
      wall:
        config:
          multi-statement-allow: true


logging:
  level:
    org.javatop: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"

ds 中是我們的所有數(shù)據(jù)源。master 是默認(rèn)的數(shù)據(jù)源,不可修改,其他的數(shù)據(jù)源可以修改并添加多個(gè)。

3.準(zhǔn)備數(shù)據(jù)庫(kù)

我這里需要提前準(zhǔn)備兩個(gè)數(shù)據(jù)庫(kù),一個(gè)是主數(shù)據(jù)庫(kù)master,一個(gè)是從數(shù)據(jù)庫(kù)slave。

我們會(huì)后面會(huì)通過(guò)一個(gè)自定義注解去實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)庫(kù)。

這里給出我們創(chuàng)建的一個(gè)user表的SQL語(yǔ)句。

CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

4.2 加載數(shù)據(jù)源

我們可以通過(guò)@ConfigurationProperties 注解加載定義的配置文件。spring.datasource 對(duì)應(yīng)的注解都會(huì)匹配到。

package org.javatop.dynamic.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;

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

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:10
 * @description :
 */
@ConfigurationProperties(prefix = "spring.datasource")
public class DruidProperties {
    private String type;
    private String driverClassName;
    private Map<String, Map<String,String>> ds;

    private Integer initialSize;
    private Integer minIdle;
    private Integer maxActive;
    private Integer maxWait;

    /**
     *一會(huì)在外部構(gòu)建好一個(gè) DruidDataSource 對(duì)象,包含三個(gè)核心屬性 url、username、password
     * 在這個(gè)方法中設(shè)置公共屬性
     * @param druidDataSource
     * @return
     */
    public DataSource dataSource(DruidDataSource druidDataSource){
        druidDataSource.setInitialSize(initialSize);
        druidDataSource.setMinIdle(minIdle);
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setMaxWait(maxWait);
        return druidDataSource;
    }
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public Map<String, Map<String, String>> getDs() {
        return ds;
    }

    public void setDs(Map<String, Map<String, String>> ds) {
        this.ds = ds;
    }

    public Integer getInitialSize() {
        return initialSize;
    }

    public void setInitialSize(Integer initialSize) {
        this.initialSize = initialSize;
    }

    public Integer getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(Integer minIdle) {
        this.minIdle = minIdle;
    }

    public Integer getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(Integer maxActive) {
        this.maxActive = maxActive;
    }

    public Integer getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(Integer maxWait) {
        this.maxWait = maxWait;
    }
}

然后我們開(kāi)始通過(guò)進(jìn)行加載DruidProperties來(lái)加載數(shù)據(jù)源。

@EnableConfigurationProperties :這個(gè)注解的意思是使 ConfigurationProperties 注解生效。

package org.javatop.dynamic.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

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

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:12
 * @description : 加載數(shù)據(jù)源
 */

@Component
@EnableConfigurationProperties(DruidProperties.class)
public class LoadDataSource {

    @Autowired
    DruidProperties druidProperties;

    public Map<String, DataSource> loadAllDataSource()   {
        Map<String, DataSource> map =new HashMap<>();
        Map<String, Map<String, String>> ds = druidProperties.getDs();
        try {
            Set<String> keySet = ds.keySet();
            for (String key : keySet) {
                map.put(key, druidProperties.dataSource((DruidDataSource) DruidDataSourceFactory.createDataSource(ds.get(key))));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

loadAllDataSource() 方法可以通過(guò)讀取application.yml配置文件中所有數(shù)據(jù)源對(duì)象。(我們這里有一個(gè)master主數(shù)據(jù)庫(kù),和一個(gè)slave從數(shù)據(jù)庫(kù))

druidProperties.dataSource(DruidDataSource druidDataSource) 這個(gè)方法為每個(gè)數(shù)據(jù)源配置其他額外的屬性(最大連接池等信息)。

DruidDataSourceFactory.createDataSource(ds.get(key):創(chuàng)建一個(gè)數(shù)據(jù)源,賦予三個(gè)核心的屬性。(username、url、password)

最終,所有的數(shù)據(jù)源都會(huì)存入map中。

4.3 自定義ThreadLocal工具類

我們這里定義一個(gè)簡(jiǎn)單的ThreadLocal工具類

package org.javatop.dynamic.utils;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:13
 * @description : ThreadLocal工具類
 */
public class DynamicDataSourceUtil {

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

    public static void setDataSourceType(String dsType){
        CONTEXT_HOLDER.set(dsType);
    }
    public static String getDataSourceType(){
        return CONTEXT_HOLDER.get();
    }

    public static void clear(){
        CONTEXT_HOLDER.remove();
    }
}

4.4 自定義注解

首先需要通過(guò)一個(gè)枚舉類來(lái)設(shè)定一下我們的默認(rèn)數(shù)據(jù)源,也是是master主數(shù)據(jù)庫(kù)。

package org.javatop.dynamic.constant;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:13
 * @description :
 */
public interface DataSourceType {
    String default_ds_name ="master";
}

然后自定義一個(gè)注解,后面也就是通過(guò)這個(gè)注解來(lái)動(dòng)態(tài)的配置切換我們的數(shù)據(jù)源,這里就也叫Datasource吧。

package org.javatop.dynamic.annotation;

import org.javatop.dynamic.constant.DataSourceType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:14
 * @description :  這個(gè)注解將來(lái)可以加在某一個(gè) service 類上或者方法上,通過(guò) value 屬性來(lái)指定類或者方法應(yīng)該使用哪個(gè)數(shù)據(jù)源
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DataSource{

    /**
     * 如果一個(gè)方法上加了 @DataSource 注解,但是卻沒(méi)有指定數(shù)據(jù)源的名稱,那么默認(rèn)使用 Master 數(shù)據(jù)源
     * @return
     */
    String value() default DataSourceType.default_ds_name;
}

4.5 AOP解析自定義注解

package org.javatop.dynamic.annotation;

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.javatop.dynamic.utils.DynamicDataSourceUtil;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:15
 * @description : AOP解析自定義注解
 */
@Component
@Aspect
public class DataSourceAspect {


    /**
     * @annotation(org.javatop.dynamic.annotation.DataSource) 表示方法上有 @DataSource 注解 就將方法攔截下來(lái)。
     * @within :如果類上面有 @DataSource 注解,就將類中的方法攔截下來(lái)。
     */
    @Pointcut("@annotation(org.javatop.dynamic.annotation.DataSource) || " +
            "@within(org.javatop.dynamic.annotation.DataSource)")
    public void pc(){

    }

    @Around("pc()")
    public Object around(ProceedingJoinPoint point){
        //獲取方法上面的注解
        DataSource dataSource =getDataSource(point);
        if(dataSource!=null){
            // 注解中數(shù)據(jù)源的名稱
            String value = dataSource.value();
            DynamicDataSourceUtil.setDataSourceType(value);
        }
        try {
            return point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }finally {
            DynamicDataSourceUtil.clear();
        }
        return null;
    }

    private DataSource getDataSource(ProceedingJoinPoint point) {
        /**
         * 先去查找方法上的注解,如果沒(méi)有,再去類中找。
         */
        MethodSignature signature = (MethodSignature)point.getSignature();
        DataSource annotation = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if(annotation!=null){
            return annotation;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(),DataSource.class);
    }
}

@Pointcut 定義

@Pointcut("@annotation(org.javatop.dynamic.annotation.DataSource) || " +
          "@within(org.javatop.dynamic.annotation.DataSource)")
public void pc() {

}
  • @Pointcut 是一個(gè)定義在方法上的注解,用來(lái)指定一個(gè)切點(diǎn)(即在何處進(jìn)行攔截)。
  • "@annotation(org.javatop.dynamic.annotation.DataSource)" 表示攔截所有被 @DataSource 注解標(biāo)記的方法。
  • "@within(org.javatop.dynamic.annotation.DataSource)" 表示攔截所有在類級(jí)別被 @DataSource 注解標(biāo)記的類中的方法。
  • pc() 方法本身是空的,因?yàn)樗械倪壿嫸紝⒃谂c這個(gè)切點(diǎn)相關(guān)的通知(advice)中定義。

@Around 通知

e@Around("pc()")
public Object around(ProceedingJoinPoint point) {
    DataSource dataSource = getDataSource(point);
    if (dataSource != null) {
        String value = dataSource.value();
        DynamicDataSourceUtil.setDataSourceType(value);
    }
    try {
        return point.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    } finally {
        DynamicDataSourceUtil.clear();
    }
    return null;
}
  • @Around("pc()") 表示這是一個(gè)環(huán)繞通知,它會(huì)在 pc() 方法所定義的切點(diǎn)前后執(zhí)行。
  • ProceedingJoinPoint point 是連接點(diǎn)的信息,它包含了方法的所有相關(guān)信息,如方法名、參數(shù)等。
  • getDataSource(point) 用來(lái)獲取方法或類上的 @DataSource 注解。
  • 如果存在 @DataSource 注解,它會(huì)從注解中獲取數(shù)據(jù)源的名稱,并通過(guò) DynamicDataSourceUtil.setDataSourceType(value) 設(shè)置當(dāng)前線程的數(shù)據(jù)源。
  • point.proceed() 是調(diào)用原始方法的地方。
  • finally 塊中的 DynamicDataSourceUtil.clear() 用于在方法執(zhí)行完畢后清理數(shù)據(jù)源設(shè)置,確保不會(huì)影響其他的數(shù)據(jù)庫(kù)操作。

最后獲取@DataSource注解

4.6 自定義動(dòng)態(tài)數(shù)據(jù)源

package org.javatop.dynamic.config;

import org.javatop.dynamic.constant.DataSourceType;
import org.javatop.dynamic.utils.DynamicDataSourceUtil;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

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

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:18
 * @description : 定義動(dòng)態(tài)數(shù)據(jù)源
 */
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(LoadDataSource loadDataSource) {
        // 1、設(shè)置所有的數(shù)據(jù)源
        Map<String, DataSource> stringDataSourceMap = loadDataSource.loadAllDataSource();
        super.setTargetDataSources(new HashMap<>(stringDataSourceMap));
        // 2、設(shè)置默認(rèn)的數(shù)據(jù)源
        super.setDefaultTargetDataSource(stringDataSourceMap.get(DataSourceType.default_ds_name));

        super.afterPropertiesSet();
    }

    /**
     * 這個(gè)方法用來(lái)返回?cái)?shù)據(jù)源名稱,當(dāng)系統(tǒng)需要獲取數(shù)據(jù)源的時(shí)候,會(huì)自動(dòng)調(diào)用該方法獲取數(shù)據(jù)源的名稱
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceUtil.getDataSourceType();
    }
}
  • DynamicDataSource 類擴(kuò)展自 AbstractRoutingDataSource 類,這是Spring框架提供的一個(gè)抽象類,用于實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)路由。
  • 構(gòu)造函數(shù) public DynamicDataSource(LoadDataSource loadDataSource) 接收一個(gè) LoadDataSource 類型的參數(shù)。這個(gè)參數(shù)被用于加載所有的數(shù)據(jù)源配置。
    • Map<String, DataSource> stringDataSourceMap = loadDataSource.loadAllDataSource(); 這行代碼調(diào)用了 loadDataSourceloadAllDataSource 方法來(lái)加載所有數(shù)據(jù)源配置,并將其存儲(chǔ)在一個(gè)名為 stringDataSourceMap 的Map中,其中鍵是數(shù)據(jù)源的名稱,值是對(duì)應(yīng)的 DataSource 對(duì)象。
    • super.setTargetDataSources(new HashMap<>(stringDataSourceMap)); 這行代碼設(shè)置了目標(biāo)數(shù)據(jù)源。它將前面加載的所有數(shù)據(jù)源 stringDataSourceMap 設(shè)置為目標(biāo)數(shù)據(jù)源。
    • super.setDefaultTargetDataSource(stringDataSourceMap.get(DataSourceType.default_ds_name)); 這行代碼設(shè)置了默認(rèn)的數(shù)據(jù)源。它通過(guò) DataSourceType.default_ds_namestringDataSourceMap 中獲取默認(rèn)的數(shù)據(jù)源,并設(shè)置為默認(rèn)數(shù)據(jù)源。
    • super.afterPropertiesSet(); 是一個(gè)初始化方法,確保所有屬性都被正確設(shè)置。
  • determineCurrentLookupKey() 方法是 AbstractRoutingDataSource 的一個(gè)抽象方法,必須要實(shí)現(xiàn)。這個(gè)方法用于決定使用哪個(gè)數(shù)據(jù)源,通常情況下是根據(jù)某種條件動(dòng)態(tài)返回?cái)?shù)據(jù)源名稱。
    • return DynamicDataSourceUtil.getDataSourceType(); 這行代碼返回當(dāng)前線程所使用的數(shù)據(jù)源的名稱。DynamicDataSourceUtil 是一個(gè)工具類,可能提供了線程局部變量(ThreadLocal)來(lái)存儲(chǔ)每個(gè)線程所選擇的數(shù)據(jù)源名稱。

這樣,當(dāng)應(yīng)用程序需要與數(shù)據(jù)庫(kù)進(jìn)行交互時(shí),就會(huì)通過(guò) DynamicDataSource 獲取到當(dāng)前線程所指定的數(shù)據(jù)源,并進(jìn)行相應(yīng)的數(shù)據(jù)庫(kù)操作。這種方式能夠在不同業(yè)務(wù)場(chǎng)景中靈活切換數(shù)據(jù)源,非常適合多租戶、讀寫分離等復(fù)雜的數(shù)據(jù)庫(kù)應(yīng)用場(chǎng)景。

4.7 編寫業(yè)務(wù)層

我們編寫一個(gè)service層

package org.javatop.dynamic.service;

import org.javatop.dynamic.annotation.DataSource;
import org.javatop.dynamic.domain.User;
import org.javatop.dynamic.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:26
 * @description :
 */
@Service
public class UserService{
    @Autowired
    private UserMapper userMapper;

    @DataSource("slave")
//    @DataSource
    public List<User> getAll(){
        List<User> all = userMapper.getAll();
        return all;
    }
}

我們?cè)趃etAll()方法上加上@DataSource(“slave”),并指定slave從數(shù)據(jù)庫(kù)。

然后再編寫一個(gè)mapper,去操作數(shù)據(jù)庫(kù)。

package org.javatop.dynamic.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.javatop.dynamic.domain.User;

import java.util.List;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:26
 * @description :
 */
@Mapper
public interface UserMapper {
    @Select("select * from user")
    List<User> getAll();
}

4.8 測(cè)試

package org.javatop.dynamic;

import org.javatop.dynamic.domain.User;
import org.javatop.dynamic.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

/**
 * @author : Leo
 * @version 1.0
 * @date 2024-01-02 15:32
 * @description :
 */
@SpringBootTest
public class DynamicTest {

    @Autowired
    private UserService userService;

    /**
     *  用于測(cè)試:
     */
    @Test
    public void test() {
        List<User> all = userService.getAll();
        if(all !=null){
            for (User user : all) {
                System.out.println(user);
            }
        }
    }
}

我們查看控制臺(tái)。

image-20240102193458748

image-20240102193553638

可以看出來(lái)我們?nèi)ゲ樵兊氖莟est02庫(kù)中的user數(shù)據(jù)。

大功告成!!!

5.總結(jié)

以上就是SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論