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

Java多數(shù)據(jù)源的三種實現(xiàn)方式小結

 更新時間:2025年03月31日 11:19:47   作者:黑尾土撥鼠  
多數(shù)據(jù)源是在一個應用程序中配置和使用多個不同的數(shù)據(jù)庫連接,本文主要介紹了Java多數(shù)據(jù)源的三種實現(xiàn)方式小結,具有一定的參考價值,感興趣的可以了解一下

1、什么時候會用到多數(shù)據(jù)源(Multiple Data Sources)

在Java開發(fā)中,“多數(shù)據(jù)源”指的是在一個應用程序中配置和使用多個不同的數(shù)據(jù)庫連接。通常情況下,一個Java應用程序會連接到一個單一的數(shù)據(jù)庫。然而,在一些復雜的應用場景中,可能需要訪問多個不同的數(shù)據(jù)庫,這時就需要配置多數(shù)據(jù)源。

1.1、多數(shù)據(jù)源具體含義

  • 多個數(shù)據(jù)庫實例:多數(shù)據(jù)源可以是指同一個類型(如MySQL)的多個數(shù)據(jù)庫實例。例如,一個應用程序可能需要同時連接到兩個不同的MySQL數(shù)據(jù)庫,一個用于存儲用戶信息,另一個用于存儲訂單信息。
  • 不同類型的數(shù)據(jù)庫:多數(shù)據(jù)源也可以是指不同類型的數(shù)據(jù)庫。例如,一個應用程序可能需要連接一個MySQL數(shù)據(jù)庫來存儲用戶信息,同時還需要連接一個Oracle數(shù)據(jù)庫來存儲財務數(shù)據(jù)。
  • 不同的訪問策略:多數(shù)據(jù)源配置也可能用于實現(xiàn)數(shù)據(jù)庫訪問策略的不同,比如讀寫分離(一個數(shù)據(jù)源用于寫操作,另一個用于讀操作),或者是在多租戶架構下,不同租戶使用不同的數(shù)據(jù)源。

1.2、為什么需要多數(shù)據(jù)源

  • 業(yè)務需求:不同的業(yè)務模塊可能需要存儲在不同的數(shù)據(jù)庫中。例如,財務數(shù)據(jù)和用戶數(shù)據(jù)可能分別存儲在兩個獨立的數(shù)據(jù)庫中,以便更好地進行管理和安全控制。
  • 系統(tǒng)架構:在大型分布式系統(tǒng)中,通常會使用分庫分表來應對海量數(shù)據(jù)的挑戰(zhàn),不同的數(shù)據(jù)庫實例可能分布在不同的服務器上。
  • 讀寫分離:為了提高系統(tǒng)的性能,尤其是在高并發(fā)場景下,常見的做法是將數(shù)據(jù)庫的讀操作和寫操作分開,讀操作可以從多個只讀的從庫(Slave)中獲取數(shù)據(jù),而寫操作則寫入到主庫(Master)。
  • 遷移或兼容性:在系統(tǒng)遷移或升級的過程中,可能需要同時訪問新舊兩個數(shù)據(jù)庫系統(tǒng),或者需要兼容不同版本的數(shù)據(jù)庫。
  • 多租戶支持:在SaaS應用中,每個租戶的數(shù)據(jù)可能被隔離存儲在不同的數(shù)據(jù)庫中,以確保數(shù)據(jù)的獨立性和安全性。

2、Java中實現(xiàn)多數(shù)據(jù)源的三種方式

2.1 基于Spring提供的AbstractRoutingDataSource

使用 Spring 提供的 AbstractRoutingDataSource 實現(xiàn)多數(shù)據(jù)源配置是一種動態(tài)數(shù)據(jù)源管理的有效方法。通過繼承 AbstractRoutingDataSource,可以根據(jù)某些條件(如請求上下文、當前線程信息等)動態(tài)地選擇要使用的數(shù)據(jù)源。這種方式非常適合需要根據(jù)業(yè)務邏輯動態(tài)切換數(shù)據(jù)源的場景,如讀寫分離、分庫分表等。
基于 AbstractRoutingDataSource 實現(xiàn)多數(shù)據(jù)源配置的詳細步驟如下:

2.1.1 創(chuàng)建一個 DynamicDataSource 類

首先,創(chuàng)建一個繼承 AbstractRoutingDataSource 的類,來實現(xiàn)數(shù)據(jù)源的動態(tài)切換邏輯。

@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
    // 當前使用的數(shù)據(jù)源標識
    public static ThreadLocal<String> name=new ThreadLocal<>();
    // 寫數(shù)據(jù)源
    @Autowired
    DataSource dataSource1;
    // 讀數(shù)據(jù)源
    @Autowired
    DataSource dataSource2;

    @Override
    protected Object determineCurrentLookupKey() {
        return name.get();
    }
    @Override
    public void afterPropertiesSet() {
        // 為targetDataSources初始化所有數(shù)據(jù)源
        Map<Object, Object> targetDataSources=new HashMap<>();
        targetDataSources.put("W",dataSource1);
        targetDataSources.put("R",dataSource2);

        super.setTargetDataSources(targetDataSources);

        // 為defaultTargetDataSource 設置默認的數(shù)據(jù)源
        super.setDefaultTargetDataSource(dataSource1);

        super.afterPropertiesSet();

    }

}

2.1.2 配置數(shù)據(jù)源

接下來,我們需要配置多個數(shù)據(jù)源。

@Configuration
public class DataSourceConfig {
	/**
		數(shù)據(jù)庫1的配置
	/
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1")
    public DataSource dataSource1() {
        // 底層會自動拿到spring.datasource中的配置, 創(chuàng)建一個DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

	/**
		數(shù)據(jù)庫2的配置
	/
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")
    public DataSource dataSource2() {
        // 底層會自動拿到spring.datasource中的配置, 創(chuàng)建一個DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    public DataSourceTransactionManager transactionManager1(DynamicDataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

    @Bean
    public DataSourceTransactionManager transactionManager2(DynamicDataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

2.1.3 自定義注解,便于區(qū)別不同數(shù)據(jù)源

在業(yè)務層,我們可以通過注解設置當前數(shù)據(jù)源的標識符來實現(xiàn)數(shù)據(jù)源的動態(tài)切換。

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {
    String value() default "W";
}

在業(yè)務層,我們可以通過注解設置當前數(shù)據(jù)源的標識符來實現(xiàn)數(shù)據(jù)源的動態(tài)切換。將自己實現(xiàn)的DynamicDataSource注冊成為默認的DataSource實例后,只需要在每次使用DataSource時,提前改變一下其中的name標識,就可以快速切換數(shù)據(jù)源。

@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {
    // 前置
    @Before("within(org.arkham.dynamic_datasource.service.impl.*) && @annotation(wr)")
    public void before(JoinPoint point, WR wr){
        String name = wr.value();
        DynamicDataSource.name.set(name);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

2.1.4 Service通過注解設置數(shù)據(jù)源

在業(yè)務層具體使用方法上增加注解,實現(xiàn)數(shù)據(jù)源切換

@Service
public class UserImplService implements UserService {

    @Autowired
    UserMapper userMapper;


    @Override
    @WR("R")        // 庫1
    public List<User> list() {
        return userMapper.list();
    }

    @Override
    @WR("W")        // 庫2
    public void save(User friend) {
        userMapper.save(friend);
    }
}

2.2 使用MyBatis注冊多個SqlSessionFactory

在Spring 環(huán)境中,如果有多個數(shù)據(jù)源并且需要為每個數(shù)據(jù)源配置單獨的 SqlSessionFactory 和SqlSessionTemplate,可以通過配置多個 SqlSessionFactory 實現(xiàn)這一點。如果使用MyBatis框架,要注冊多個數(shù)據(jù)源的話,就需要將MyBatis底層的DataSource、SqlSessionFactory、DataSourceTransactionManager這些核心對象一并進行手動注冊

基于 MyBatis 注冊多個 SqlSessionFactory實現(xiàn)多數(shù)據(jù)源配置的詳細步驟如下:

2.2.1 不同數(shù)據(jù)源注冊

數(shù)據(jù)源1:

@Configuration
// 繼承mybatis:
// 1. 指定掃描的mapper接口包(主庫)
// 2. 指定使用sqlSessionFactory是哪個(主庫)
@MapperScan(basePackages = "org.arkham.dynamic_datasource_mybatis.mapper.w",
        sqlSessionFactoryRef="wSqlSessionFactory")
public class WMyBatisConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource1")
    public DataSource dataSource1() {
        // 底層會自動拿到spring.datasource中的配置, 創(chuàng)建一個DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public SqlSessionFactory wSqlSessionFactory()
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 指定主庫
        sessionFactory.setDataSource(dataSource1());
        // 可以手動指定主庫對應的mapper.xml文件
        /*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/order/*.xml"));*/
        return sessionFactory.getObject();
    }

    @Bean
    @Primary
    public DataSourceTransactionManager wTransactionManager(){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource1());
        return dataSourceTransactionManager;
    }


    @Bean
    public TransactionTemplate wTransactionTemplate(){
        return new TransactionTemplate(wTransactionManager());
    }
}

數(shù)據(jù)源2:

@Configuration
// 繼承mybatis:
// 1. 指定掃描的mapper接口包(主庫)
// 2. 指定使用sqlSessionFactory是哪個(主庫)
@MapperScan(basePackages = "org.arkham.dynamic_datasource_mybatis.mapper.r",
        sqlSessionFactoryRef="rSqlSessionFactory")
public class RMyBatisConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.datasource2")
    public DataSource dataSource2() {
        // 底層會自動拿到spring.datasource中的配置, 創(chuàng)建一個DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public SqlSessionFactory rSqlSessionFactory()
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 指定主庫
        sessionFactory.setDataSource(dataSource2());
        // 可以手動指定主庫對應的mapper.xml文件
        /*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/r/*.xml"));*/
        return sessionFactory.getObject();
    }



    @Bean
    public DataSourceTransactionManager rTransactionManager(){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource2());
        return dataSourceTransactionManager;
    }

    @Bean
    public TransactionTemplate rTransactionTemplate(){
        return new TransactionTemplate(rTransactionManager());
    }

2.2.2 業(yè)務層具體使用

在應用程序中,具體的 DAO 層使用 SqlSessionFactory 和 SqlSessionTemplate 時,MyBatis 會根據(jù) @MapperScan 指定的包路徑,使用相應的數(shù)據(jù)源。

@Service
public class UserImplService implements UserService {

    @Autowired
    private RUserMapper rFriendMapper;

    @Autowired
    private WUserMapper wFriendMapper;
    // 讀-- 讀庫
    @Override
    public List<User> list() {
        return rFriendMapper.list();
    }

    // 保存-- 寫庫
    @Override
    public void save(User user) {
        wFriendMapper.save(user);
    }


    // 保存-- 寫庫
    @Override
    public void saveW(User user) {
        user.setName("xman11");
        wFriendMapper.save(user);
    }

    // 保存-- 讀庫
    @Override
    public void saveR(User user) {
        user.setName("xman");
        rFriendMapper.save(user);
    }
}

2.3 使用dynamic-datasource框架

使用 dynamic-datasource-spring-boot-starter 框架可以非常方便地實現(xiàn) Java 項目中的多數(shù)據(jù)源配置。這個框架能夠支持多種數(shù)據(jù)源的自動切換、動態(tài)數(shù)據(jù)源的配置管理等功能。
基于 dynamic-datasource-spring-boot-starter 框架實現(xiàn)多數(shù)據(jù)源配置的詳細步驟如下:

2.3.1 添加依賴

首先,需要在 pom.xml 文件中添加 dynamic-datasource-spring-boot-starter 的依賴。

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version> <!-- 請根據(jù)項目需要調整版本 -->
</dependency>

2.3.2 配置數(shù)據(jù)源

在application.yml 文件中配置多個數(shù)據(jù)源??梢越o每個數(shù)據(jù)源起一個唯一的名稱(例如 master 和 slave)

spring:
  datasource:
    dynamic:
      primary: master # 默認數(shù)據(jù)源或數(shù)據(jù)源組,當未指定數(shù)據(jù)源時會使用該數(shù)據(jù)源
      strict: false # 設置為true表示檢查配置文件中數(shù)據(jù)源是否有效
      datasource:
        master: # 數(shù)據(jù)源一
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/master_db
          username: master_user
          password: master_pass
        slave: # 數(shù)據(jù)源二
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/slave_db
          username: slave_user
          password: slave_pass

2.3.3 配置數(shù)據(jù)源

使用 @DS 注解來指定某個方法或類使用特定的數(shù)據(jù)源。

@Service
public class UserImplService implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override
    @DS("master")
    public void save(User user) {
        userMapper.save(user);
    }
    @Override
    @DS("slave_1")  // 從庫, 如果按照下劃線命名方式配置多個 , 可以指定前綴即可(組名)
    public List<User> list() {
        return userMapper.list();
    }
}

對于 MyBatis,dynamic-datasource 同樣適用。只需正常配置 MyBatis,并在 Mapper 方法上使用 @DS 注解即可。

import com.baomidou.dynamic.datasource.annotation.DS;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {

    @DS("master")
    @Select("SELECT * FROM user WHERE id = #{id}")
    User getUserFromMaster(int id);

    @DS("slave")
    @Select("SELECT * FROM user WHERE id = #{id}")
    User getUserFromSlave(int id);
}

dynamic-datasource 框架提供了一種簡潔、高效的方式來實現(xiàn)多數(shù)據(jù)源的配置和動態(tài)切換。通過注解方式,可以輕松切換數(shù)據(jù)源,滿足業(yè)務中對多數(shù)據(jù)源的需求。

3 總結

這三種實現(xiàn)多數(shù)據(jù)源的方式各有優(yōu)缺點,適用于不同的場景,具體選擇哪種方式還需根據(jù)實際需求來判斷。此外,配置多數(shù)據(jù)源的方式不止于此,例如在使用 Spring Data JPA 時,也可以通過配置多個 EntityManager 實現(xiàn)多數(shù)據(jù)源。每個 EntityManager 可以與特定的 DataSource 關聯(lián),從而實現(xiàn)對多個數(shù)據(jù)庫的操作。因此,在多數(shù)據(jù)源的配置中,靈活選擇適合的方案至關重要。

源碼地址:https://github.com/arkhamYJ/datasource.git

到此這篇關于Java多數(shù)據(jù)源的三種實現(xiàn)方式小結的文章就介紹到這了,更多相關Java多數(shù)據(jù)源內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 深入理解Java中沒那么簡單的單例模式

    深入理解Java中沒那么簡單的單例模式

    這篇文章主要給大家詳細介紹了Java單例模式,關于Java中的單例模式并非看起來那么簡單的,為什么要這么說呢?下面通過這篇文章來一起看看吧,有需要的朋友們可以參考借鑒。
    2017-01-01
  • Java并發(fā)編程中的生產者與消費者模型簡述

    Java并發(fā)編程中的生產者與消費者模型簡述

    這篇文章主要介紹了Java并發(fā)編程中的生產者與消費者模型簡述,多線程并發(fā)是Java編程中最終要的部分之一,需要的朋友可以參考下
    2015-07-07
  • Java服務調用失敗報Service com.oneinfinite.adflow.api.service.TestService未找到的解決方法

    Java服務調用失敗報Service com.oneinfinite.adflow.api.service.T

    在Java開發(fā)中,服務調用是常見的操作,尤其是在微服務架構中,然而,服務調用過程中可能會遇到各種問題,下面我們來看看如何解決Service com.oneinfinite.adflow.api.service.TestService with version 0.0.0 not found的問題吧
    2025-03-03
  • java項目實現(xiàn)猜拳小游戲

    java項目實現(xiàn)猜拳小游戲

    這篇文章主要為大家詳細介紹了java項目實現(xiàn)猜拳小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • SpringBoot中項目如何讀取外置logback配置文件

    SpringBoot中項目如何讀取外置logback配置文件

    這篇文章主要介紹了SpringBoot中項目如何讀取外置logback配置文件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java 8中Stream API的這些奇技淫巧!你Get了嗎?

    Java 8中Stream API的這些奇技淫巧!你Get了嗎?

    這篇文章主要介紹了Java 8中Stream API的這些奇技淫巧!你Get了嗎?文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • JAVA的Dubbo如何實現(xiàn)各種限流算法

    JAVA的Dubbo如何實現(xiàn)各種限流算法

    Dubbo是一種高性能的Java RPC框架,廣泛應用于分布式服務架構中,在Dubbo中實現(xiàn)限流可以幫助服務在高并發(fā)場景下保持穩(wěn)定性和可靠性,常見的限流算法包括固定窗口算法、滑動窗口算法、令牌桶算法和漏桶算法,在Dubbo中集成限流器可以通過實現(xiàn)自定義過濾器來實現(xiàn)
    2025-01-01
  • Java中throw和throws異常處理完整例子說明

    Java中throw和throws異常處理完整例子說明

    這篇文章主要給大家介紹了關于Java中throw和throws異常處理的相關資料, throw關鍵字是用于在方法內拋出異常,而throws關鍵字是在方法聲明中指定可能拋出的異常,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-06-06
  • 如何使用Guava Cache做緩存

    如何使用Guava Cache做緩存

    Cache在ConcurrentHashMap的基礎上提供了自動加載數(shù)據(jù)、清除數(shù)據(jù)、get-if-absend-compute的功能,本文給大家介紹如何使用Guava Cache做緩存,感興趣的朋友一起看看吧
    2023-11-11
  • Java創(chuàng)建可執(zhí)行JAR文件的多種方式

    Java創(chuàng)建可執(zhí)行JAR文件的多種方式

    本文主要介紹了Java創(chuàng)建可執(zhí)行JAR文件的多種方式,使用JDK的jar工具、IDE、Maven和Gradle來創(chuàng)建和配置可執(zhí)行JAR文件,具有一定的參考價值,感興趣的可以了解一下
    2024-07-07

最新評論