您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(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ò),可以把它分享出去讓更多的人看到。
免責(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)容。