這篇文章將為大家詳細講解有關springboot中怎么利用mybatis實現數據庫的讀寫分離,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
首先,我們需要兩個數據庫實例,一為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-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
實現
整個實現主要有3個部分:
配置兩個數據源 實現AbstractRoutingDataSource來動態的使用數據源 實現mybatis plugin來動態的選擇數據源
配置數據源
將數據庫連接信息配置到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寫數據源,對應到master節點的15432端口
read讀數據源,對應到slave節點的15433端口
將兩個數據源信息注入為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(); }}
實現AbstractRoutingDataSource
spring提供了AbstractRoutingDataSource,提供了動態選擇數據源的功能,替換原有的單一數據源后,即可實現讀寫分離:
@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
AbstractRoutingDataSource內部維護了一個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
實現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使用依賴版本
關于springboot中怎么利用mybatis實現數據庫的讀寫分離就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。