# 多数据源切换
我们在项目中,有时候会碰到多个数据源切换的场景,这里从零开始说一下步骤!!也是做一个笔记备忘。
# 准备工作
事先准备好一个能走通curd的 springboot+mybatis的web项目
# 项目结构
# 主从数据库
# 保证一个数据库下接口访问正常
# 多数据源切换
我们目前已经有一个基础项目,接下来正式开始进入正题。所谓多数据源切换,无非就是对Druid,HikriCP或者其他数据源上做手脚
废话不多说,先把我们的数据库连接字符串配上去
- 配置主从数据库连接 application.yml
spring:
datasource:
druid:
biubiu-master: #主库
url: jdbc:mysql://localhost:3306/biubiu?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
biubiu-slave: #从库
url: jdbc:mysql://localhost:3306/biubiu_slave?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 把配置读到Java里,初始化数据源
- DataSourceName 数据源名称
public class DataSourceName {
private DataSourceName(){}
public static final String MASTER = "biubiu";
public static final String SLAVE = "biubiu_slave";
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- DynamicDataSource 动态数据源基类
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
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
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
- DynamicDataSourceConfig 多数据源配置类
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.biubiu-master")
public DataSource masterDataSource() throws SQLException {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.biubiu-slave")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceName.MASTER, masterDataSource);
targetDataSources.put(DataSourceName.SLAVE, slaveDataSource);
return new DynamicDataSource(masterDataSource, targetDataSources);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
到此我们数据源配置,初始化完成!!接下来,开始做切换,我们采用Aop方式做数据源切换,大致思路是 Aop拦截注解,通过注解指定的数据源做切换
- 自定义注解 AOP拦截
- DataSource注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "";
}
1
2
3
4
5
6
2
3
4
5
6
- DataSourceAspect Aop拦截
@Aspect
@Component
public class DataSourceAspect {
private static final Logger log = LoggerFactory.getLogger(DataSourceAspect.class);
@Pointcut("@annotation(com.biubiu.multidatabase.config.DataSource) " + // 方法上的注解
"|| @within(com.biubiu.multidatabase.config.DataSource)") // 类上的注解
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
//获取切入点的方法和对应的类
MethodSignature signature = (MethodSignature) point.getSignature();
Class targetClass = point.getTarget().getClass();
Method method = signature.getMethod();
//获取类和方法上的注解
DataSource targetDataSource = (DataSource) targetClass.getAnnotation(DataSource.class);
DataSource methodDataSource = method.getAnnotation(DataSource.class);
if (targetDataSource != null || methodDataSource != null) {
//获取类和方法上的注解的值方法优先于类注解
String value;
if (methodDataSource != null) {
value = methodDataSource.value();
} else {
value = targetDataSource.value();
}
DynamicDataSource.setDataSource(value);
log.debug("set datasource is {}", value);
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
log.debug("clean datasource");
}
}
}
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
31
32
33
34
35
36
37
38
39
40
41
42
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
31
32
33
34
35
36
37
38
39
40
41
42
- 开始使用 在Controller/Service/Mapper里可以都可以试试
@RequestMapping("/loadMaster")
@DataSource(name = DataSourceName.MASTER)
public Object loadMaster(int id) {
return userService.load(id);
}
@RequestMapping("/loadSlave")
@DataSource(name = DataSourceName.SLAVE)
public Object loadSlave(int id) {
return userService.load(id);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11