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

spring boot + mybatis如何實現(xiàn)數(shù)據(jù)庫的讀寫分離

 更新時間:2019年09月05日 09:19:17   作者:別拿豆包不當(dāng)干糧  
這篇文章主要給大家介紹了關(guān)于spring boot + mybatis如何實現(xiàn)數(shù)據(jù)庫的讀寫分離的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

介紹

隨著業(yè)務(wù)的發(fā)展,除了拆分業(yè)務(wù)模塊外,數(shù)據(jù)庫的讀寫分離也是常見的優(yōu)化手段。

方案使用了AbstractRoutingDataSource和mybatis plugin來動態(tài)的選擇數(shù)據(jù)源

選擇這個方案的原因主要是不需要改動原有業(yè)務(wù)代碼,非常友好

注:

demo中使用了mybatis-plus,實際使用mybatis也是一樣的
demo中使用的數(shù)據(jù)庫是postgres,實際任一類型主從備份的數(shù)據(jù)庫示例都是一樣的
demo中使用了alibaba的druid數(shù)據(jù)源,實際其他類型的數(shù)據(jù)源也是一樣的

環(huán)境

首先,我們需要兩個數(shù)據(jù)庫實例,一為master,一為slave。

所有的寫操作,我們在master節(jié)點(diǎn)上操作

所有的讀操作,我們在slave節(jié)點(diǎn)上操作

需要注意的是:對于一次有讀有寫的事務(wù),事務(wù)內(nèi)的讀操作也不應(yīng)該在slave節(jié)點(diǎn)上,所有操作都應(yīng)該在master節(jié)點(diǎn)上
先跑起來兩個pg的實例,其中15432端口對應(yīng)的master節(jié)點(diǎn),15433端口對應(yīng)的slave節(jié)點(diǎn):

docker run \
 --name pg-master \
 -p 15432:5432 \
 --env 'PG_PASSWORD=postgres' \
 --env 'REPLICATION_MODE=master' \
 --env 'REPLICATION_USER=repluser' \
  --env 'REPLICATION_PASS=repluserpass' \
 -d sameersbn/postgresql:10-2

docker run \
 --name pg-slave \
 -p 15433:5432 \
 --link pg-master:master \
 --env 'PG_PASSWORD=postgres' \
 --env 'REPLICATION_MODE=slave' \
 --env 'REPLICATION_SSLMODE=prefer' \
 --env 'REPLICATION_HOST=master' \
 --env 'REPLICATION_PORT=5432' \
 --env 'REPLICATION_USER=repluser' \
  --env 'REPLICATION_PASS=repluserpass' \
 -d sameersbn/postgresql:10-2

實現(xiàn)

整個實現(xiàn)主要有3個部分:

  • 配置兩個數(shù)據(jù)源
  • 實現(xiàn)AbstractRoutingDataSource來動態(tài)的使用數(shù)據(jù)源
  • 實現(xiàn)mybatis plugin來動態(tài)的選擇數(shù)據(jù)源

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

將數(shù)據(jù)庫連接信息配置到application.yml文件中

spring:
 mvc:
  servlet:
   path: /api

datasource:
 write:
  driver-class-name: org.postgresql.Driver
  url: "${DB_URL_WRITE:jdbc:postgresql://localhost:15432/postgres}"
  username: "${DB_USERNAME_WRITE:postgres}"
  password: "${DB_PASSWORD_WRITE:postgres}"
 read:
  driver-class-name: org.postgresql.Driver
  url: "${DB_URL_READ:jdbc:postgresql://localhost:15433/postgres}"
  username: "${DB_USERNAME_READ:postgres}"
  password: "${DB_PASSWORD_READ:postgres}"


mybatis-plus:
 configuration:
  map-underscore-to-camel-case: true

write寫數(shù)據(jù)源,對應(yīng)到master節(jié)點(diǎn)的15432端口

read讀數(shù)據(jù)源,對應(yīng)到slave節(jié)點(diǎn)的15433端口

將兩個數(shù)據(jù)源信息注入為DataSourceProperties:

@Configuration
public class DataSourcePropertiesConfig {

  @Primary
  @Bean("writeDataSourceProperties")
  @ConfigurationProperties("datasource.write")
  public DataSourceProperties writeDataSourceProperties() {
    return new DataSourceProperties();
  }

  @Bean("readDataSourceProperties")
  @ConfigurationProperties("datasource.read")
  public DataSourceProperties readDataSourceProperties() {
    return new DataSourceProperties();
  }
}

實現(xiàn)AbstractRoutingDataSource

spring提供了AbstractRoutingDataSource,提供了動態(tài)選擇數(shù)據(jù)源的功能,替換原有的單一數(shù)據(jù)源后,即可實現(xiàn)讀寫分離:

@Component
public class CustomRoutingDataSource extends AbstractRoutingDataSource {

  @Resource(name = "writeDataSourceProperties")
  private DataSourceProperties writeProperties;

  @Resource(name = "readDataSourceProperties")
  private DataSourceProperties readProperties;


  @Override
  public void afterPropertiesSet() {
    DataSource writeDataSource = 
      writeProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
    DataSource readDataSource = 
      readProperties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
    
    setDefaultTargetDataSource(writeDataSource);

    Map<Object, Object> dataSourceMap = new HashMap<>();
    dataSourceMap.put(WRITE_DATASOURCE, writeDataSource);
    dataSourceMap.put(READ_DATASOURCE, readDataSource);
    setTargetDataSources(dataSourceMap);

    super.afterPropertiesSet();
  }

  @Override
  protected Object determineCurrentLookupKey() {
    String key = DataSourceHolder.getDataSource();

    if (key == null) {
       // default datasource
      return WRITE_DATASOURCE;
    }

    return key;
  }

}

AbstractRoutingDataSource內(nèi)部維護(hù)了一個Map<Object, Object>的Map

在初始化過程中,我們將write、read兩個數(shù)據(jù)源加入到這個map

調(diào)用數(shù)據(jù)源時:determineCurrentLookupKey()方法返回了需要使用的數(shù)據(jù)源對應(yīng)的key

當(dāng)前線程需要使用的數(shù)據(jù)源對應(yīng)的key,是在DataSourceHolder類中維護(hù)的:

public class DataSourceHolder {

  public static final String WRITE_DATASOURCE = "write";
  public static final String READ_DATASOURCE = "read";

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


  public static void putDataSource(String dataSource) {
    local.set(dataSource);
  }

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

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

}

實現(xiàn)mybatis plugin

上面提到了當(dāng)前線程使用的數(shù)據(jù)源對應(yīng)的key,這個key需要在mybatis plugin根據(jù)sql類型來確定
MybatisDataSourceInterceptor類:

@Component
@Intercepts({
    @Signature(type = Executor.class, method = "update",
        args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
            CacheKey.class, BoundSql.class})})
public class MybatisDataSourceInterceptor implements Interceptor {

  @Override
  public Object intercept(Invocation invocation) throws Throwable {

    boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
    if(!synchronizationActive) {
      Object[] objects = invocation.getArgs();
      MappedStatement ms = (MappedStatement) objects[0];

      if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
        DataSourceHolder.putDataSource(DataSourceHolder.READ_DATASOURCE);
      }
    }

    return invocation.proceed();
  }

  @Override
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  @Override
  public void setProperties(Properties properties) {
  }
}

僅當(dāng)未在事務(wù)中,并且調(diào)用的sql是select類型時,在DataSourceHolder中將數(shù)據(jù)源設(shè)為read

其他情況下,AbstractRoutingDataSource會使用默認(rèn)的write數(shù)據(jù)源

至此,項目已經(jīng)可以自動的在讀、寫數(shù)據(jù)源間切換,無需修改原有的業(yè)務(wù)代碼

最后,提供demo使用依賴版本

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.7.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.2</version>
  </dependency>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.9</version>
  </dependency>
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatisplus-spring-boot-starter</artifactId>
    <version>1.0.5</version>
  </dependency>
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>2.1.9</version>
  </dependency>
  <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.8.0</version>
  </dependency>
  <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.8.0</version>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 淺談spring 常用注解

    淺談spring 常用注解

    這篇文章主要介紹了淺談spring 常用注解,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • MyBatis將查詢出的兩列數(shù)據(jù)裝配成鍵值對的操作方法

    MyBatis將查詢出的兩列數(shù)據(jù)裝配成鍵值對的操作方法

    這篇文章主要介紹了MyBatis將查詢出的兩列數(shù)據(jù)裝配成鍵值對的操作代碼,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • SpringCloud2020整合Nacos-Bootstrap配置不生效的解決

    SpringCloud2020整合Nacos-Bootstrap配置不生效的解決

    這篇文章主要介紹了SpringCloud2020整合Nacos-Bootstrap配置不生效的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 圖文詳解Java中class的初始化順序

    圖文詳解Java中class的初始化順序

    網(wǎng)上有很多關(guān)于Java中class的初始化順序文章,但是本文通過圖文更加詳細(xì)的介紹了Java中class的初始化順序,并對class的裝載順序進(jìn)行了講解,下面一起來看看。
    2016-08-08
  • SpringBoot全局Controller返回值格式統(tǒng)一

    SpringBoot全局Controller返回值格式統(tǒng)一

    本文主要介紹了SpringBoot全局Controller返回值格式統(tǒng)一,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • SpringBoot替換默認(rèn)的tomcat服務(wù)器的方法

    SpringBoot替換默認(rèn)的tomcat服務(wù)器的方法

    Tomcat是Apache基金下的一個輕量級的Servlet容器,支持Servlet和JSP,Tomcat具有Web服務(wù)器特有的功能,在SpringBoot框架中,我們使用最多的是Tomcat,這是SpringBoot默認(rèn)的容器技術(shù),本文給大家介紹了Spring?Boot如何替換默認(rèn)的tomcat服務(wù)器,需要的朋友可以參考下
    2024-08-08
  • java Servlet 實現(xiàn)動態(tài)驗證碼圖片示例

    java Servlet 實現(xiàn)動態(tài)驗證碼圖片示例

    這篇文章主要介紹了java Servlet 實現(xiàn)動態(tài)驗證碼圖片示例的資料,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。
    2017-02-02
  • mybatis水平分表實現(xiàn)動態(tài)表名的項目實例

    mybatis水平分表實現(xiàn)動態(tài)表名的項目實例

    本文主要介紹了mybatis水平分表實現(xiàn)動態(tài)表名的項目實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 如何解決java獲取時間相差8小時的問題

    如何解決java獲取時間相差8小時的問題

    最近使用new date()獲取的時間會和真實的本地時間相差8小時。本文就詳細(xì)的來介紹一下解決java獲取時間相差8小時的問題,感興趣的可以了解一下
    2021-09-09
  • Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換詳解

    Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換詳解

    這篇文章主要給大家介紹了關(guān)于Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11

最新評論