简介
基于spring的AbstractRoutingDataSource接口实现的多数据源路由功能,实现不同数据源之间切换
Git地址
https://gitee.com/wqrzsy/lp-demo/tree/master/lp-datasource-router
更多demo请关注
springboot demo实战项目
java 脑洞
java 面试宝典
开源工具
项目分析
- 多数据源配置
spring:
datasource:
d1:
platform: mysql
jdbc-url: jdbc:mysql://localhost:13306/test1?serverTimezone=GMT&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
d2:
platform: mysql
# HikariDataSource
jdbc-url: jdbc:mysql://localhost:13306/test2?serverTimezone=GMT&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
配置类DataSourceConfiguration
通过@ConfigurationProperties注解实现不同属性注入DataSource,通过 @Primary注解标识DynamicRouterDataSource类,那其他地方拿DataSource时拿到就是代理过的DynamicRouterDataSource类
@Bean(value = FIRST_DATASOURCE_KEY)
@Qualifier(FIRST_DATASOURCE_KEY)
// ConfigurationProperties 指定属性前缀,忽略那些不能绑定到 @ConfigurationProperties 类字段的属性
@ConfigurationProperties(prefix = "spring.datasource.d1")
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(value = SECOND_DATASOURCE_KEY)
@Qualifier(SECOND_DATASOURCE_KEY)
// ConfigurationProperties 指定属性前缀,忽略那些不能绑定到 @ConfigurationProperties 类字段的属性
@ConfigurationProperties(prefix = "spring.datasource.d2")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Primary
@Bean(name = "routingDataSource")
public AbstractRoutingDataSource routingDataSource(
@Qualifier(FIRST_DATASOURCE_KEY) DataSource d1,
@Qualifier(SECOND_DATASOURCE_KEY) DataSource d2
) {
DynamicRouterDataSource proxy = new DynamicRouterDataSource();
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put("masterDataSource", d1);
targetDataSources.put("slaveDataSource", d2);
proxy.setDefaultTargetDataSource(d1);
proxy.setTargetDataSources(targetDataSources);
DataSourceRouterContext.setDefaultDataSource("masterDataSource");
return proxy;
}
- 准备路由注解
/**
* 数据库路由注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Transactional(propagation = Propagation.REQUIRES_NEW)
public @interface DataSourceRouter {
String value();
}
- 路由AOP,这里@Order(-1)是关键,要优先@Transactional切换,在事务之前就切换好数据库
/**
* 数据库路由AOP
*/
@Aspect
@Component
@Order(-1)
public class DataSourceRouterAOP {
private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceRouterAOP.class);
/**
* 此处的切点是注解的方式,也可以用包名的方式达到相同的效果
* '@Pointcut("execution(* com.wwj.springboot.service.impl.*.*(..))")'
*/
@Pointcut("@annotation(DataSourceRouter)")
public void delayInvoke() {
}
/**
* 环绕增强,相当于MethodInterceptor
*/
@Around("delayInvoke()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取DataSourceRouter 注解获取数据源名字
DataSourceRouter dataSourceRouter = method.getAnnotation(DataSourceRouter.class);
// 通过DataSourceRouterContext指定当前数据源
DataSourceRouterContext.setDataSource(dataSourceRouter.value());
try {
// 调用方法
return joinPoint.proceed();
} finally {
// 清空环境
DataSourceRouterContext.clear();
}
}
}
4.调用实例
/**
* 测试路由服务
*/
@Service
public class TestRouterService {
@Autowired
private TestEntityDAO testEntityDAO;
@DataSourceRouter(value = "masterDataSource")
public void testD1() {
TestEntity entity = TestEntity.of(System.currentTimeMillis(), "123", 1);
testEntityDAO.save(entity);
}
@DataSourceRouter(value = "slaveDataSource")
public void testD2() {
TestEntity entity = TestEntity.of(System.currentTimeMillis(), "123", 1);
testEntityDAO.save(entity);
}
}
注意
- 使用@DataSourceRouter时,会每次都会新建一个事务,这里使用时需注意
-
需要建立数据库test1和test2,然后导入resources下的test.sql文件建表
image.png
demo项目导入
参考: https://www.jianshu.com/p/cd0275a2f5fb
公众号
五分钟了解前沿技术,大数据,微服务,区域链,提供java前沿技术干货,独立游戏制作技术分享
五分钟技术如果这篇文章对你有帮助请给个star
image.png
网友评论