您好,登录后才能下订单哦!
这篇文章将为大家详细讲解有关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<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>
关于springboot中怎么利用mybatis实现数据库的读写分离就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。