搞定SpringBoot多數據源(1):多套源策略

tags: multi-datasource java springbootmysql


一句話歸納:Spring Boot開發中鏈接多個數據庫進行讀寫操做,使用多套數據源是最直接、簡單的方式。git

1. 引言

在開發過程當中,避免不了須要同時操做多個數據庫的狀況,一般的應用場景以下 :github

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

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

  • 多套數據源:即針對一個數據庫創建一套數據處理邏輯,每套數據庫都包括數據源配置、會話工廠( sessionFactory )、鏈接、SQL 操做、實體。各套數據庫相互獨立。
  • 動態數據源:肯定數量的多個數據源共用一個會話工廠,根據條件動態選取數據源進行鏈接、SQL 操做。
  • 參數化變動數據源:根據參數添加數據源,並進行數據源切換,數據源數量不肯定。一般用於對多個數據庫的管理工做。

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

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

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 目錄中獲取。apache

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 進行多數據源操做,最後對多套數據源的優缺點進行總結。但願小夥伴們能夠對多數據源操做有個初步印象。

本文有配套的示例代碼,有興趣的能夠跑一下示例來感覺一下。

參考資料

往期文章

個人公衆號(搜索Mason技術記錄),獲取更多技術記錄:

mason

相關文章
相關標籤/搜索