SpringBoot+AbstractRoutingDataSource实现多数据源以及事务处理

一、多数据源的使用场景

实际开发中,经常可能遇到在一个应用中可能需要访问多个数据库的情况。以下是两种典型场景:

1.1 业务复杂/数据量大

一个应用需要用到不同库的数据,就需要连不同的数据库。
比如我们的项目里,一个服务,同时需要用自己mysql表的数据,还要连上大数据的库获取数据。
在这里插入图片描述

1.2 读写分离

为了解决数据库的读性能瓶颈(读比写性能更高, 写锁会影响读阻塞,从而影响读的性能)。
很多数据库拥主从架构。也就是,一台主数据库服务器,是对外提供增删改业务的生产服务器;另一(多)台从数据库服务器,主要进行
读的操作。ꞏ
可以通过中间件(ShardingSphere、mycat、mysql-proxy 、TDDL …), 但是有一些规模较小的公司,没有专门的中间件团队搭建读写分
离基础设施,因此需要业务开发人员自行实现读写分离
在这里插入图片描述
这里的架构与上图类似。不同的是,在读写分离中,主库和从库的数据库是一致的(不考虑主从延迟)。数据更新操作(insert、update、
delete)都是在主库上进行,主库将数据变更信息同步给从库。在查询时,可以在从库上进行,从而分担主库的压力。

二、如何实现多数据源

2.1 AbstractRoutingDataSource原理

spring框架中,spring-jdbc模块提供了AbstractRoutingDataSource,其内部可以包含了多个DataSource,然后在运行时来动态的访问哪个数据库。这种方式访问数据库的架构图如下所示:

在这里插入图片描述
应用直接操作的是AbstractRoutingDataSource的实现类,告诉AbstractRoutingDataSource访问哪个数据库,然后由AbstractRoutingDataSource从事先配置好的数据源(ds1、ds2)选择一个,来访问对应的数据库。
在这里插入图片描述

1.当执行数据库持久化操作,只要集成了Spring就一定会通过DataSourceUtils获取Connection
2. 通过Spring注入的DataSource获取Connection 即可执行数据库操作
所以思路就是:只需配置一个实现了DataSource的Bean, 然后根据业务动态提供Connection即可
3.其实Spring已经提供一个DataSource实现类用于动态切换数据源——AbstractRoutingDataSource
4.分析AbstractRoutingDataSource即可实现动态数据源切换:

通过这个类可以实现动态数据源切换。如下是这个类的成员变量

 private Map<Object, Object> targetDataSources;
 private Object defaultTargetDataSource;
 private Map<Object, DataSource> resolvedDataSources;
  • targetDataSources 保存了key和数据库连接的映射关系 (所有数据源)
  • defaultTargetDataSource标识默认的连接(默认数据源)
  • resolvedDataSources这个数据结构是通过targetDataSources构建而来,存储结构也是数据库标识和数据源的映射关系

而AbstractRoutingDataSource实现了InitializingBean接口,并实现了afterPropertiesSet方法。afterPropertiesSet方法是初始化bean的时候执行,通常用作数据初始化。resolvedDataSources就是在这里赋值

 @Override
 public void afterPropertiesSet() {
   
   //...
 	this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size()); //初始化resolvedDataSources
 	//循环targetDataSources,并添加到resolvedDataSources中
	 for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
   
	 	Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
		DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
	 	this.resolvedDataSources.put(lookupKey, dataSource);
	 }
 //...
 }

5.所以,我们只需创建AbstractRoutingDataSource实现类DynamicDataSource然后 始化targetDataSources和key为数据源标识(可以是字符串、枚举、都行,因为标识是Object)、defaultTargetDataSource即可
6.后续当调用AbstractRoutingDataSource.getConnection 会接着调用提供的模板方法:determineTargetDataSource

7.通过determineTargetDataSource该方法返回的数据库标识 从resolvedDataSources 中拿到对应的数据源
8.所以,我们只需DynamicDataSource中实现determineTargetDataSource为其提供一个数据库标识

总结: 在整个代码中我们只需做4件大事:
1.定义AbstractRoutingDataSource实现类DynamicDataSource
2. 初始化时为targetDataSources设置 不同数据源的DataSource和标识、及defaultTargetDataSource
3. 在determineTargetDataSource中提供对应的数据源标识即可
4、切换数据源标识

2.2 通过AbstractRoutingDataSource实现动态数据源

1 . 配置多数据源 和 AbstractRoutingDataSource的自定义实现类:DynamicDataSource
配置多数据

server:
  port: 8080

spring:
  datasource:
    druid:
      ds1:
        url: jdbc:mysql://localhost:3306/db_ds1?serverTimeZone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
        username: root
        password: root
        driver-class-name: com.mysql.jdbc.Driver
      ds2:
        url: jdbc:mysql://localhost:3306/db_ds2?serverTimeZone=UTC&useUnicode=true&characterEncoding=UTF-8&a
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值