本项目使用 Spring Boot 和 MyBatis 实现多数据源,动态数据源的切换;有多种不同的实现方式,在学习的过程中发现没有文章将这些方式和常见的问题集中处理,所以将常用的方式和常见的问题都写在了在本项目的不同分支上:
master: 使用了多数据源的 RESTful API 接口,使用 Druid 实现了 DAO 层数据源动态切换和只读数据源负载均衡dev: 最简单的切面和注解方式实现的动态数据源切换druid: 通过切面和注解方式实现的使用 Druid 连接池的动态数据源切换aspect_dao: 通过切面实现的 DAO 层的动态数据源切换roundrobin: 通过切面使用轮询方式实现的只读数据源负载均衡以上分支都是基于 dev 分支修改或扩充而来,基本涵盖了常用的多数据源动态切换的方式,基本的原理都一样,都是通过切面根据不同的条件在执行数据库操作前切换数据源
在使用的过程中基本踩遍了所有动态数据源切换的坑,将常见的一些坑和解决方法写在了 Issues 里面
该项目使用了一个可写数据源和多个只读数据源,为了减少数据库压力,使用轮循的方式选择只读数据源;考虑到在一个 Service 中同时会有读和写的操作,所以本应用使用 AOP 切面通过 DAO 层的方法名切换只读数据源;但这种方式要求数据源主从一致,并且应当避免在同一个 Service 方法中写入后立即查询,如果必须在执行写入操作后立即读取,应当在 Service 方法上添加 @Transactional 注解以保证使用主数据源
需要注意的是,使用 DAO 层切面后不应该在 Service 类层面上加 @Transactional 注解,而应该添加在方法上,这也是 Spring 推荐的做法
动态切换数据源依赖 configuration 包下的4个类来实现,分别是:DataSourceRoutingDataSource.javaDataSourceConfigurer.javaDynamicDataSourceContextHolder.javaDynamicDataSourceAspect.java
添加依赖
.
创建数据库及表
分别创建数据库product_master,product_slave_alpha,product_slave_beta,product_slave_gamma在以上数据库中分别创建表product,并插入不同数据
.
.
配置数据源
application.properties
.
.
.
配置数据源
DataSourceKey.java
.
DataSourceRoutingDataSource.java该类继承自AbstractRoutingDataSource类,在访问数据库时会调用该类的determineCurrentLookupKey()方法获取数据库实例的 key
.
DataSourceConfigurer.java数据源配置类,在该类中生成多个数据源实例并将其注入到ApplicationContext中
.
.
.
.
DynamicDataSourceContextHolder.java该类为数据源上下文配置,用于切换数据源
.
.
.
.
DynamicDataSourceAspect.java动态数据源切换的切面,切 DAO 层,通过 DAO 层方法名判断使用哪个数据源,实现数据源切换关于切面的 Order 可以可以不设,因为 @Transactional 是最低的,取决于其他切面的设置,并且在 org.springframework.core.annotation.AnnotationAwareOrderComparator 会重新排序
.
.
配置 Product REST API 接口
ProductController.java
.
.
ProductService.java
.
.
ProductDao.java
.
ProductMapper.xml启动项目,此时访问 /product/1 会返回 product_master 数据库中 product 表中的所有数据,多次访问 /product 会分别返回 product_slave_alpha、product_slave_beta、product_slave_gamma 数据库中 product 表中的数据,同时也可以在看到切换数据源的 log,说明动态切换数据源是有效的
注意
在该应用中因为使用了 DAO 层的切面切换数据源,所以 @Transactional 注解不能加在类上,只能用于方法;有 @Trasactional注解的方法无法切换数据源
.