动态数据源

订单迁移TIDB 实现读写分离

目标

  • 读写分离

准备工作

  • 数据源准备
  • 动态数据源切换
    • 参考 AbstractRoutingDataSource

配置数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//DruidDataSourceBuilder自行引入starter包
@Configuration
@Component
public class DynamicDataSourceConfig {

@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource master(){
return DruidDataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slave(){
return DruidDataSourceBuilder.create().build();
}

@Bean
@Primary
public DynamicDataSource dataSource(DataSource master, DataSource slave) {
Map<Object, Object> allDataSources = new HashMap<>();
targetDataSources.put("master",master);
targetDataSources.put("slave", slave);
return new DynamicDataSource(master, allDataSources);
}
}

动态数据源切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DynamicDataSource  extends AbstractRoutingDataSource {

private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> allDataSources) {
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(allDataSources);
super.afterPropertiesSet();
}

@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}

public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}

public static String getDataSource() {
return contextHolder.get();
}

public static void clearDataSource() {
contextHolder.remove();
}
}

配置切换注解

1
2
3
4
5
6
7
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}

AOP切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Aspect
@Component
public class DataSourceAspect {

@Pointcut("@annotation(xx.xx.xx.DataSource)")
public void dataSourcePointCut() {

}

@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();

DataSource dataSource = method.getAnnotation(DataSource.class);
if(dataSource == null){
// 如果方法上没有加注解. 默认导向到master去
DynamicDataSource.setDataSource("master");
}else {
// 如果方法上加了注解,导向到标记的数据源
DynamicDataSource.setDataSource(dataSource.name());
}
try {
return point.proceed();
} finally {
// 执行结束后清除thread local中的记录
DynamicDataSource.clearDataSource();
}
}
}

使用方式

1
2
3
4
5
6
7
8
/**
* 查询master库User,下面的master可以考虑定义成常量
*/
@DataSource(name="master")
public List<TestUser> getMasterUser(){
QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>();
return testUserMapper.selectAll(queryWrapper.isNotNull("name"));
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!