亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

spring boot + mybatis實現數據庫讀寫分離的示例

發布時間:2021-02-03 11:12:39 來源:億速云 閱讀:277 作者:小新 欄目:編程語言

小編給大家分享一下spring boot + mybatis實現數據庫讀寫分離的示例,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

介紹

隨著業務的發展,除了拆分業務模塊外,數據庫的讀寫分離也是常見的優化手段。

方案使用了AbstractRoutingDataSource和mybatis plugin來動態的選擇數據源

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

注:

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

環境

首先,我們需要兩個數據庫實例,一為master,一為slave。

所有的寫操作,我們在master節點上操作

所有的讀操作,我們在slave節點上操作

需要注意的是:對于一次有讀有寫的事務,事務內的讀操作也不應該在slave節點上,所有操作都應該在master節點上
先跑起來兩個pg的實例,其中15432端口對應的master節點,15433端口對應的slave節點:

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

實現

整個實現主要有3個部分:

  • 配置兩個數據源

  • 實現AbstractRoutingDataSource來動態的使用數據源

  • 實現mybatis plugin來動態的選擇數據源

配置數據源

將數據庫連接信息配置到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寫數據源,對應到master節點的15432端口

read讀數據源,對應到slave節點的15433端口

將兩個數據源信息注入為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();
  }
}

實現AbstractRoutingDataSource

spring提供了AbstractRoutingDataSource,提供了動態選擇數據源的功能,替換原有的單一數據源后,即可實現讀寫分離:

@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內部維護了一個Map<Object, Object>的Map

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

調用數據源時:determineCurrentLookupKey()方法返回了需要使用的數據源對應的key

當前線程需要使用的數據源對應的key,是在DataSourceHolder類中維護的:

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();
  }

}

實現mybatis plugin

上面提到了當前線程使用的數據源對應的key,這個key需要在mybatis plugin根據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) {
  }
}

僅當未在事務中,并且調用的sql是select類型時,在DataSourceHolder中將數據源設為read

其他情況下,AbstractRoutingDataSource會使用默認的write數據源

至此,項目已經可以自動的在讀、寫數據源間切換,無需修改原有的業務代碼

最后,提供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>

看完了這篇文章,相信你對“spring boot + mybatis實現數據庫讀寫分離的示例”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

屏东市| 乾安县| 阿鲁科尔沁旗| 黔东| 波密县| 绍兴市| 苍梧县| 平果县| 昌乐县| 平南县| 兴仁县| 乳源| 新干县| 马龙县| 团风县| 营山县| 股票| 岑溪市| 海伦市| 永仁县| 宁强县| 巴里| 健康| 历史| 阿克| 通化县| 泗洪县| 旺苍县| 靖西县| 丹东市| 建水县| 独山县| 龙里县| 峨眉山市| 萨迦县| 大冶市| 洪江市| 新兴县| 依安县| 遂宁市| 万源市|