1. 程式人生 > >搞定SpringBoot多資料來源(1):多套源策略

搞定SpringBoot多資料來源(1):多套源策略

目錄

  • 1. 引言
  • 2. 執行環境
  • 3. 多套資料來源
    • 3.1 搭建 Spring Boot 工程
      • 3.1.1 初始化 Spring Boot 工程
      • 3.1.2 新增 MyBatis Plus 依賴
      • 3.1.3 新增包結構
    • 3.2 多套資料來源
      • 3.2.1 獨立資料庫連線資訊
      • 3.2.2 多套資料來源配置
      • 3.2.3 多套實體
      • 3.2.4 多套Mapper操作
      • 3.2.5 多套 mapper xml 檔案
    • 3.3 多資料來源使用
  • 4. 優缺點
    • 4.1 優點
    • 4.2 缺點
  • 5. 總結
  • 參考資料
  • 往期文章

tags: multi-datasource java springboot


一句話概括:Spring Boot開發中連線多個數據庫進行讀寫操作,使用多套資料來源是最直接、簡單的方式。

1. 引言

在開發過程中,避免不了需要同時操作多個數據庫的情況,通常的應用場景如下 :

  • 資料庫高效能場景:主從,包括一主一從,一主多從等,在主庫進行增刪改操作,在從庫進行讀操作。
  • 資料庫高可用場景:主備,包括一往一備,多主多備等,在資料庫無法訪問時可以切換。
  • 同構或異構資料的業務處理:需要處理的資料儲存在不同的資料庫中,包括同構(如都是 MySQL ),異構(如一個MySQL ,另外是 PG 或者 Oracle )。

使用 Spring Boot 該如何處理多個數據庫的讀寫,一般有以下幾種策略:

  • 多套資料來源:即針對一個數據庫建立一套資料處理邏輯,每套資料庫都包括資料來源配置、會話工廠( sessionFactory )、連線、SQL 操作、實體。各套資料庫相互獨立。
  • 動態資料來源:確定數量的多個數據源共用一個會話工廠,根據條件動態選取資料來源進行連線、SQL 操作。
  • 引數化變更資料來源:根據引數新增資料來源,並進行資料來源切換,資料來源數量不確定。通常用於對多個數據庫的管理工作。

本系列文章“搞定SpringBoot多資料來源”將針對以上幾個策略進行描述,本文是第一篇:“多套資料來源”,主要以主從場景為例項,結合程式碼,對多套資料來源的實現進行描述,內容包括搭建 Spring Boot + MyBatis Plus 工程、多資料來源配置、多資料來源處理與使用邏輯。

本文所涉及到的示例程式碼:https://github.com/mianshenglee/my-example/tree/master/multi-datasource,讀者可結合一起看。

2. 執行環境

  • JAVA 執行環境: JDK1.8
  • Spring Boot: 2.2.2.RELEASE
  • MyBatis Plus: 3.3.0
  • 開發IDE: IDEA
  • 構建工具Maven: 3.3.9
  • MySQL : 5.6.26
  • Lombok: 1.18.10

3. 多套資料來源

多套資料來源,顧名思義每一個數據庫有一套獨立的操作。從下往上,資料庫、會話工廠、DAO操作,服務層都是獨立的一套,如下所示:

本示例中,以一主一從兩個資料庫,兩資料庫的分別有一個表 test_user,表結構一致,為便於說明,兩個表中的資料是不一樣的。兩個表結構可在示例程式碼中的 sql 目錄中獲取。

3.1 搭建 Spring Boot 工程

3.1.1 初始化 Spring Boot 工程

使用 spring.io構建初始 Spring Boot 工程,選用以下幾個構件:

  • Lombok: 用於簡化操作
  • Spring Configuration Processor:配置檔案處理器
  • Spring Web: 用於構建web服務
  • MySQL Driver: 資料庫驅動

3.1.2 新增 MyBatis Plus 依賴

MyBatis Plus 是對 MyBatis 增強,簡化 DAO 操作,提高資料庫操作效率。依賴如下:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.0</version>
</dependency>

3.1.3 新增包結構

主要新增以下幾個包:

├─config ---------------------------------- // 資料來源配置
├─controller ------------------------------ // web服務
├─entity ---------------------------------- // 實體類
│ ├─master 
│ └─slave 
├─mapper ---------------------------------- // dao操作類
│ ├─master 
│ └─slave 
└─vo -------------------------------------- // 檢視返回物件  

注:

  • 由於示例簡單,省略service層
  • 實體類及mapper均根據主從進行劃分

3.2 多套資料來源

3.2.1 獨立資料庫連線資訊

Spring Boot 的預設配置檔案是 application.properties ,由於有兩個資料庫配置,獨立配置資料庫是好的實踐,因此新增配置檔案 jbdc.properties ,新增以下自定義的主從資料庫配置:

# master
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/mytest?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.master.username=root
spring.datasource.master.password=111111

# slave
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/my_test1?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.slave.username=root
spring.datasource.slave.password=111111

3.2.2 多套資料來源配置

有了資料來源連線資訊,需要把資料來源注入到 Spring 中。由於每個資料庫使用獨立的一套資料庫連線,資料庫連線使用的 SqlSession 進行會話連線,SqlSession 是由SqlSessionFactory 生成。因此,需要分別配置SqlSessionFactory 。以下操作均在 config 目錄 下:

(1)新增 DataSourceConfig 配置檔案,注入主從資料來源

@Configuration
@PropertySource("classpath:config/jdbc.properties")
public class DatasourceConfig {
    @Bean("master")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean("slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource(){
        return DataSourceBuilder.create().build();
    }
}
  • 註解 PropertySource 指定配置資訊檔案
  • 註解 ConfigurationProperties 指定主從配置字首
  • 分別指定主從資料來源的 bean 名稱為 masterslave

(2)新增 MasterMybatisConfig 配置檔案,注入 Master 的SqlSessionFactory

@Configuration
@MapperScan(basePackages = "me.mason.demo.basicmultidatasource.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterMybatisConfig {
    /**
     * 注意,此處需要使用MybatisSqlSessionFactoryBean,不是SqlSessionFactoryBean,
     * 否則,使用mybatis-plus的內建函式時就會報invalid bound statement (not found)異常
     */
    @Bean("masterSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("master") DataSource dataSource) throws Exception {
        // 設定資料來源
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        //mapper的xml檔案位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = "classpath*:/mapper/master/*.xml";
        mybatisSqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        //對應資料庫的entity位置
        String typeAliasesPackage = "me.mason.demo.basicmultidatasource.entity.master";
        mybatisSqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
        return mybatisSqlSessionFactoryBean.getObject();
    }
}
  • 註解 MapperScan 指定那些包下的 mapper 使用本資料來源,並指定使用哪個SqlSessionFactory,注意,此處的 sqlSessionFactoryRef 即本配置中的注入的 SqlSessionFactory
  • 設定指定的資料來源,此處是名為 master 的資料來源,使用 Qualifier 指定。
  • MyBatis Plus 對應的 Mapper 若有自定義的 mapper.xml, 則使用 setMapperLocations 指定。
  • 若需要對實體進行別名處理,則使用 setTypeAliasesPackage 指定。

(3)新增 SlaveMybatisConfig 配置檔案,注入Slave 的 SqlSessionFactory

與(2)一致,把 master 改為 slave即可。

3.2.3 多套實體

在 MyBatis 配置中,實體設定 typeAliases 可以簡化 xml 的配置,前面提到,使用 typeAliasesPackage 設定實體路徑,在 entity 包下分別設定 masterslave 包,存放兩個庫對應的表實體,使用 Lombok 簡化實體操作。如下:

@Data
@TableName("test_user")
public class MasterTestUser implements Serializable {
    private static final long serialVersionUID = 1L;
    /** id */
    private Long id;
    /** 姓名 */
    private String name;
    ...
}

3.2.4 多套Mapper操作

mapper 包下,分別新增 masterslave 包,存放兩個庫對應的 Mapper ,由於 MyBatis Plus 本身已包含基本的 CRUD 操作,所以很多時候可以不用 xml 檔案配置。若需要自定義操作,需要結合 xml檔案,與此同時需要指定對應的 xml 檔案所在目錄。如下:

@Repository
public interface MasterTestUserMapper extends BaseMapper<MasterTestUser> {
    /**
     * 自定義查詢
     * @param wrapper 條件構造器
     */
    List<MasterTestUser> selectAll(@Param(Constants.WRAPPER)Wrapper<MasterTestUser> wrapper);
}

slave對應的Mapper與此類似

3.2.5 多套 mapper xml 檔案

MyBatis Plus 的預設mapper xml 檔案路徑為 classpath*:/mapper/**/*.xml,即 resources/mapper 下,同樣設定 masterslave 目錄,分別存放對應的mapper xml 檔案。以下是 master 的自定義操作:

<mapper namespace="me.mason.demo.basicmultidatasource.mapper.master.MasterTestUserMapper">
    <select id="selectAll" resultType="masterTestUser">
        select * from test_user
        <if test="ew!=null">
          ${ew.customSqlSegment}
        </if>
    </select>
</mapper>

3.3 多資料來源使用

經過上面的多套資料來源配置,可知道,若需要操作哪個資料庫,直接使用對應的 mapper 進行 CRUD 操作即可。如下為 Controller 中分別查詢兩個庫,獲取到的資料合在一起返回:

@RestController
@RequestMapping("/user")
public class TestUserController {

    @Autowired
    private MasterTestUserMapper masterTestUserMapper;
    @Autowired
    private SlaveTestUserMapper slaveTestUserMapper;
    /**
     * 查詢全部
     */
    @GetMapping("/listall")
    public Object listAll() {
        //master庫,自定義介面查詢
        QueryWrapper<MasterTestUser> queryWrapper = new QueryWrapper<>();
        List<MasterTestUser> resultData = masterTestUserMapper.selectAll(queryWrapper.isNotNull("name"));
        //slave庫,mp內建介面
        List<SlaveTestUser> resultDataSlave = slaveTestUserMapper.selectList(null);
        //返回
        Map<String, Object> result = new HashMap<>();
        result.put("master" , resultData);
        result.put("slave" , resultDataSlave);
        return ResponseResult.success(result);
    }

}
  • 使用Autowired註解注入對應的mapper
  • 使用對應資料庫的 mapper 進行業務操作
  • 根據業務在資料庫中執行相應的操作,如主只做增刪改操作、從只讀操作

至此,多資料來源的實現已完成,當前示例是兩個同構的資料庫,當然,若是異構的資料庫,或者多於兩個的資料庫,處理方式是一樣的,只不過是把資料來源增加一套而已。

4. 優缺點

由上述說明,我們可以總結一下使用多套資料來源的方法進行多資料庫操作,它的優缺點是什麼。

4.1 優點

  • 簡單、直接:一個庫對應一套處理方式,很好理解。
  • 符合開閉原則( OCP ):開發的設計模式告訴我們,對擴充套件開放,對修改關閉,新增多一個數據庫,原來的那一套不需要改動,只新增即可。

4.2 缺點

  • 資源浪費:針對每一個數據源寫一套操作,連線資料庫的資源也是獨立的,分別佔用同樣多的資源。SqlSessionFactory 是一個工廠,建議是使用單例,完全可以重用,不需要建立多個,只需要更改資料來源即可,跟多執行緒,使用執行緒池減少資源消耗是同一道理。
  • 程式碼冗餘:在前面的多資料來源配置中可以看出,其實 master 和 slave 的很多操作是一樣的,只是改個名稱而已,因此會造成程式碼冗餘。
  • 缺乏靈活:所有需要使用的地方都需要引入對應的mapper,對於很多操作,只是選擇資料來源的不一樣,程式碼邏輯是一致的。另外,對於一主多從的情況,若需要對多個從庫進行負載均衡,相對比較麻煩。

正因為有上述的缺點,所以還有改進的空間。於是就有了動態資料來源,至於動態資料來源如何實現,下回分解。

5. 總結

本文對多個數據庫的操作進行了初步探討,並對使用多套源的策略進行講解,結合主從程式碼示例,搭建了 Spring Boot + MyBatis Plus 工程,配置多資料來源及使用 Mapper 進行多資料來源操作,最後對多套資料來源的優缺點進行總結。希望小夥伴們可以對多資料來源操作有個初步印象。

本文有配套的示例程式碼,有興趣的可以跑一下示例來感受一下。

參考資料

  • Spring主從資料庫的配置和動態資料來源切換原理: https://www.liaoxuefeng.com/article/1182502273240832
  • 多資料來源與動態資料來源的權衡: https://juejin.im/post/5b790a866fb9a019ea01f38c
  • 談談Spring Boot 資料來源載入及其多資料來源簡單實現: https://juejin.im/post/5cb0023d5188250df17d4ffc
  • Spring Boot 和 MyBatis 實現多資料來源、動態資料來源切換: https://juejin.im/post/5a927d23f265da4e7e10d740

往期文章

  • java開發必學知識:動態代理
  • 2019 讀過的好書推薦
  • springboot+apache前後端分離部署https
  • springboot+logback 日誌輸出企業實踐(下)
  • springboot+logback 日誌輸出企業實踐(上)

我的公眾號(搜尋Mason技術記錄),獲取更多技術記錄:

相關推薦

SpringBoot資料來源(1)策略

目錄 1. 引言 2. 執行環境 3. 多套資料來源 3.1 搭建 Spring Boot 工程 3.1.1 初始化 Spring Boot 工程 3.1.2 新增 MyBat

SpringBoot資料來源(3)引數化變更

目錄 1. 引言 2. 引數化變更源說明 2.1 解決思路 2.2 流程說明 3. 實現引數化變更源 3.1 改造動態資料來源 3.1.1 動

springBoot(18)賬號輪詢發送郵件

spring boot 多賬號 一、添加依賴<!-- mail --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-b

1線程並發方案的不足——響應式Spring的道法術器

響應式編程本系列文章索引《響應式Spring的道法術器》本篇內容是響應式流的附錄。 (以下接響應式流的1.2.1.1節,關於“CPU眼中的時間”的內容。請不要單獨看這一篇內容,否則有些內容可能讓你摸不著頭腦 0..0) 多線程的方式有其不完美之處,而且有些難以駕馭—— 一、耗時的上下文切換 CPU先生不太樂

mybatis資料來源錯誤org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

使用Spring Boot2.0整合mybatis多資料來源遇到如下錯誤 錯誤資訊: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.boot.mapper.one.Sc

caioj1472: 字尾自動機1個串的LCS

子串母串跑合並答案 一個點的fail的dep是比任意一條根到這個點路徑長度要小的。 那麼改就可以直接來了。 #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib&

六天懂“深度學習”之三層神經網路

為了克服單層神經網路只能解決線性可分問題的侷限性,神經網路進化為多層結構。然而,花費了將近30年的時間,才將隱藏層新增到單層神經網路中。很難理解為什麼花費了這麼長時間,其中的主要問題是學習規則。 單層神經網路中的增量規則對於多層神經網路的訓練是無效的,這是因為訓練中在隱藏層產生的誤差並沒

nginx反向代理cas server之1個cas server負載均衡配置以及ssl配置

系統環境採用centOS7 由於cas server不支援session持久化方式的共享,所以請用其他方式代替,例如:組播複製。 nginx反向代理完整配置(兩個網站例項) user nobody nobody; worker_processes 2; #error

Hibernate 學習心得1 關係中,中間表無法插入資料。

Hibernate 學習心得之一 多對多關係中,中間表無法插入資料。最近學習 spring4+hibernate4,學習中遇到了很多坑。在這裡我來說說我遇到的坑,這裡就不介紹如何spring如何整合hibernate。目前學習過程中,我遇到的兩個問題1.為何在hibernat

springBoot 動態資料來源以及Mybatis資料來源

原文地址:https://blog.csdn.net/tengxing007/article/details/78424645前言在開發過程中可能需要用到多個數據源,比如一個專案(MySQL)就是和(SQL Server)混合使用,就需要使用多資料來源;如果業務場景比較復炸,

線程線程設計模式(三)Master-Worker模式

fonts strong stat bre not 多線程 too () 部分 Master-Worker模式是常用的並行模式之一,它的核心思想是,系統有兩個進程協作工作:Master進程,負責接收和分配任務;Worker進程,負責處理子任務。當Worker進程將子任務處理

分散式鎖, 註解形式, SpringBoot定時任務@Scheduled 在叢集下的優化

SpringBoot提供了 Schedule模組完美支援定時任務的執行 在實際開發中由於專案部署在分散式或叢集伺服器上 會導致定時任務多次觸發 因此,使用redis分佈鎖機制可以有效避免多次執行定時任務   核心方法是org.springframework.data.redis

Java執行緒執行緒基礎

多執行緒基礎 多執行緒實現-Thread和Runnable 通常使用如下程式碼啟動一個新的執行緒: private void startNewThread1() { new Thread() { @Override pub

MySQL表3表查詢操作

轉載:https://blog.csdn.net/Anarkh_Lee/article/details/79856935 1.交叉連線查詢(基本不會使用——得到的是兩個表的乘積) 語法:select * from A,B; 2.內連線查詢

我的畢設終於了!題為利用Python開發一款遊戲!

    現在來看一下實現的過程。 外形 俄羅斯方塊整個介面分為兩部分,一部分是左邊的遊戲區域,另一部分是右邊的顯示區域,顯示得分、速度、下一個方塊樣式等。這裡就不放截圖了,看上圖就可以。 遊戲區域跟貪吃蛇一樣,是由一個個小方格組成的,為了看得直觀,

10分鐘輕鬆SpringBoot整合RabbitMQ教程

第一步:在專案pom.xml檔案中,新增pring-boot-starter-amqp 依賴<dependency> <groupId>org.springframework.boot</groupId> <artifac

十分鐘SpringBoot 和Redis 實戰整合

這裡,對於springboot和redis 不做介紹,大家可以自己去查詢資料~~~~~~~~~~~ windows下本地安裝 redis 安裝 話不多說,來就是開搞! 專案框架 具體實現程式碼 pom檔案 <?xml version="1.0" en

一週MPU6050Linux驅動(1

第一日 準備工作: 1、硬體平臺 firefly-rk3288開發板,MPU6500六軸陀螺儀模組 2、參考 開幹: 1、搭建開發環境 首先,搭建firefly-rk3288開發板的核心編譯平臺、原始碼樹等。意思就是需要在ubuntu系統中下載firefly-rk32

一個figure存在個legend列圖例

問題:如何在Matlab中,將圖例繪製成多行多列的? 查詢資料過程中發現, 1)有的方法要求多個plot中橫座標具有相同的長度 2)高版本中,部分函式報錯(應該可以改正) 本文根據需要找到一個可靠的解決方法,現整理如下: 優點:每個plot互相獨立;程式碼量

執行緒 執行緒共享區域性變數的方法

1. 操作相同時,寫一個 Runnable 實現類,內部設定成員變數,run 方法修改該變數,將該Runnable傳給不同Thread使用; 2. 操作不同時,在Thread呼叫類例項化一個數據實例,傳遞給不同Runnable處理,再把不同的Runnable傳給不同Thre