利用Spring的AbstractRoutingDataSource解决多数据源的读写分离问题
背景
最近项目中为了提高数据库读写速度,想要横向扩展Oracle数据库,一个Master,多个Slave。master可以读写数据,Slave只能读数据。这就是多数据源问题了。怎么利用Spring解决这个问题呢?
测试代码
1. pom.xml
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.2</version><relativePath /></parent><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
2. 定义两个数据源名字
public enum ClientDatabase {CLIENT_A, CLIENT_B
}
3、利用Threadlocal 数据源的名字绑定到当前执行的线程上
public class ClientDatabaseContextHolder {private static ThreadLocal<ClientDatabase> CONTEXT= new ThreadLocal<>();public static void set(ClientDatabase clientDatabase) {Assert.notNull(clientDatabase, "clientDatabase cannot be null");CONTEXT.set(clientDatabase);}public static ClientDatabase getClientDatabase() {return CONTEXT.get();}public static void clear() {CONTEXT.remove();}
}
4. 继承AbstractRoutingDataSource ,Spring会在取数据源之前调用determineCurrentLookupKey方法取数据源的名字,通过数据源的名字查找数据源。这样就可以实现动态更改数据源了。readonly事务的时候,就可以切换到读的那个数据库了。
public class ClientDataSourceRouter extends AbstractRoutingDataSource {private static final Logger log = LoggerFactory.getLogger(ClientDataSourceRouter.class);@Overrideprotected Object determineCurrentLookupKey() {ClientDatabase clientDatabase = ClientDatabaseContextHolder.getClientDatabase();if (clientDatabase != null) { //有指定切换数据源切换的时候,才给输出日志 并且也只给输出成debug级别的 否则日志太多了log.info("线程[{}],此时切换到的数据源为:{}", Thread.currentThread().getId(), clientDatabase);}return clientDatabase;}
}
5. 定义两个数据源,不同的H2数据库。
@Configuration
public class RoutingTestConfiguration {@Autowiredprivate ClientADetails clientADetails;@Autowiredprivate ClientBDetails clientBDetails;@Beanpublic DataSource clientDatasource() {Map<Object, Object> targetDataSources = new HashMap<>();DataSource clientADatasource = clientADatasource();DataSource clientBDatasource = clientBDatasource();targetDataSources.put(ClientDatabase.CLIENT_A, clientADatasource);targetDataSources.put(ClientDatabase.CLIENT_B, clientBDatasource);ClientDataSourceRouter clientRoutingDatasource = new ClientDataSourceRouter();clientRoutingDatasource.setTargetDataSources(targetDataSources);clientRoutingDatasource.setDefaultTargetDataSource(clientADatasource);return clientRoutingDatasource;}private DataSource clientADatasource() {EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder();return dbBuilder.setType(EmbeddedDatabaseType.H2).setName(clientADetails.getName()).addScript(clientADetails.getScript()).build();}private DataSource clientBDatasource() {EmbeddedDatabaseBuilder dbBuilder = new EmbeddedDatabaseBuilder();return dbBuilder.setType(EmbeddedDatabaseType.H2).setName(clientBDetails.getName()).addScript(clientBDetails.getScript()).build();}
}
6. 创建两个类取两个数据的名字等等。
@Component
@ConfigurationProperties(prefix = "client-a.datasource")
public class ClientBDetails {private String name = "CLIENT_B";private String script = "SOME_SCRIPT.sql";// Getters & Setters
}@Component
@ConfigurationProperties(prefix = "client-a.datasource")
public class ClientADetails {private String name = "CLIENT_A";private String script = "SOME_SCRIPT.sql";// Getters & Setters
}
7. application.properties
#database details for CLIENT_A
client-a.datasource.name=CLIENT_A
client-a.datasource.script=SOME_SCRIPT.sql#database details for CLIENT_B
client-b.datasource.name=CLIENT_B
client-b.datasource.script=SOME_SCRIPT.sql
8. 测试类
@SpringJUnitConfig
@ContextConfiguration(classes = {RoutingTestConfiguration.class, ClientBDetails.class, ClientADetails.class})
public class TestSpringBean {@Autowiredprivate DataSource dataSource;@Testpublic void test1() throws SQLException {System.out.println(dataSource.getConnection());ClientDatabaseContextHolder.set(ClientDatabase.CLIENT_B);System.out.println(dataSource.getConnection());}
}
执行结果
conn2: url=jdbc:h2:mem:CLIENT_A user=SA
19:29:04.576 [main] INFO ClientDataSourceRouter - 线程[1],此时切换到的数据源为:CLIENT_B
[jdbc:h2:mem:CLIENT_B;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false]
conn3: url=jdbc:h2:mem:CLIENT_B user=SA
利用Spring的AbstractRoutingDataSource解决多数据源的读写分离问题相关推荐
- 简单好用!利用Spring AOP技术10分钟实现一个读写分离方案
点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多 作者 ...
- Spring Boot 多数据源(读写分离)入门
转载自 芋道 Spring Boot 多数据源(读写分离)入门 1. 概述 在项目中,我们可能会碰到需要多数据源的场景.例如说: 读写分离:数据库主节点压力比较大,需要增加从节点提供读操作,以减少压 ...
- Java 阿里巴巴数据源_阿里P7教你如何使用 Spring 配置动态数据源实现读写分离
最近搭建的一个项目需要实现数据源的读写分离,在这里将代码进行分享,以供参考. 关键词:DataSource .AbstractRoutingDataSource.AOP 首先是配置数据源 //配置省略 ...
- MyBatis多数据源配置(读写分离)
MyBatis多数据源配置(读写分离) 首先说明,本文的配置使用的最直接的方式,实际用起来可能会很麻烦. 实际应用中可能存在多种结合的情况,你可以理解本文的含义,不要死板的使用. 多数据源的可能情况 ...
- jpa 自定义sql if_SpringBoot整合JPA实现多数据源及读写分离
SpringBoot整合JPA实现多数据源及读写分离 项目地址:https://github.com/baojingyu/spring-boot-jpa-dynamic-datasource 本项目使 ...
- 2 数据源配置_论多数据源(读写分离)的实现方案
好的,作为一个合格的bug生产者,我们直接进入主题,多数据源和读写分离实现方案. 首先多数据源和读写分离什么时候我们才需要呢? 多数据源:一个单体项目过于复杂,需要操作多个业务库的时候,就需要多数据源 ...
- 使用spring aop实现业务层mysql 读写分离
2019独角兽企业重金招聘Python工程师标准>>> 1.使用spring aop 拦截机制现数据源的动态选取. import java.lang.annotation.Eleme ...
- Spring 下,关于动态数据源的事务问题的探讨
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:青石路 cnblogs.com/youzhibing ...
- 分布式数据层中间件详解:如何实现分库分表+动态数据源+读写分离
分布式数据层中间件: 1.简介: 分布式数据访问层中间件,旨在为供一个通用数据访问层服务,支持MySQL动态数据源.读写分离.分布式唯一主键生成器.分库分表.动态化配置等功能,并且支持从客户端角度对数 ...
最新文章
- React项目 --ES6 语法中的class (9)
- [2019HDU多校第一场][HDU 6590][M. Code]
- 再读王永庆卖米的故事
- 【Java】常见的异常和Throwable类
- 多线程爬虫191023
- 【MVC5】对MySql数据库使用EntityFramework
- lnmp升级PHP环境
- visio业务流程图教学_用visio软件怎样画数据流程图和业务流程图?
- 腾讯QQ之下载的安装包在哪里
- 通过关键词采集百度网址脚本
- 全球顶尖的程序化交易模型
- 微积分review 极限,迫敛性,极限四则运算,自然常数来历
- 精读-软件测试的艺术之调试,极限测试和因特尔应用系统的测试
- Adobe Acrobat 虚拟打印机安装方法
- 2023年4月中国数据库排行榜:达梦厚积薄发夺探花,亚信、星环勇毅笃行有突破
- 如何写网络舆情数据分析报告的技巧及注意事项详解
- 【程序】Marvell 88W8801 WiFi模块连接路由器,并使用lwip2.0.3建立http服务器(20180807版)
- SVA 断言 note
- 深圳内推 | 华为诺亚方舟实验室招聘计算机视觉算法研究员/实习生
- Carthage与CocoaPods的区别和使用步骤