溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

springboot中怎么利用mybatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)的讀寫(xiě)分離

發(fā)布時(shí)間:2021-08-10 17:07:43 來(lái)源:億速云 閱讀:146 作者:Leah 欄目:編程語(yǔ)言

這篇文章將為大家詳細(xì)講解有關(guān)springboot中怎么利用mybatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)的讀寫(xiě)分離,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

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

所有的寫(xiě)操作,我們?cè)趍aster節(jié)點(diǎn)上操作

所有的讀操作,我們?cè)趕lave節(jié)點(diǎn)上操作

需要注意的是:對(duì)于一次有讀有寫(xiě)的事務(wù),事務(wù)內(nèi)的讀操作也不應(yīng)該在slave節(jié)點(diǎn)上,所有操作都應(yīng)該在master節(jié)點(diǎn)上先跑起來(lái)兩個(gè)pg的實(shí)例,其中15432端口對(duì)應(yīng)的master節(jié)點(diǎn),15433端口對(duì)應(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-2docker 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

實(shí)現(xiàn)

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

配置兩個(gè)數(shù)據(jù)源  實(shí)現(xiàn)AbstractRoutingDataSource來(lái)動(dòng)態(tài)的使用數(shù)據(jù)源  實(shí)現(xiàn)mybatis plugin來(lái)動(dòng)態(tài)的選擇數(shù)據(jù)源

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

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

spring: mvc:  servlet:   path: /apidatasource: 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寫(xiě)數(shù)據(jù)源,對(duì)應(yīng)到master節(jié)點(diǎn)的15432端口

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

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

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

實(shí)現(xiàn)AbstractRoutingDataSource

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

@Componentpublic 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ù)了一個(gè)Map<Object, Object>的Map

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

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

當(dāng)前線(xiàn)程需要使用的數(shù)據(jù)源對(duì)應(yīng)的key,是在DataSourceHolder類(lèi)中維護(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();  }}

實(shí)現(xiàn)mybatis plugin

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

@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類(lèi)型時(shí),在DataSourceHolder中將數(shù)據(jù)源設(shè)為read

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

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

最后,提供demo使用依賴(lài)版本

<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>

關(guān)于springboot中怎么利用mybatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)的讀寫(xiě)分離就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI