进学阁

业精于勤荒于嬉,行成于思毁于随

0%

实现用户配置数据源引用切换

需求来源

实现代码生成器的时候因为做了分库,所以在实现代码生成时就需要根据不同的配置去拉取不同的表结构,所以需要实现不同的库使用不同的数据源

实现思路

在mybatis中是以DataSource的形式来管理数据源的,在mybaitis-plus中预留了AbstractRoutingDataSource接口来实现数据源的切换与查找

targetDataSources就是数据源的结合,如果我们需要增加数据源就需要在targetDataSources中增加新定义的数据源,并通知引用规则,实现如下

代码

首先我们要定义一个节点用于确定使用targetDataSources中的哪个数据源,定义DataSourceContextHolder,用于实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DataSourceContextHolder {

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

public static void setDataSourceKey(String dataSourceKey) {
contextHolder.set(dataSourceKey);
}

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

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

定义DynamicDataSource继承与AbstractRoutingDataSource

1
2
3
4
5
6
7
8
9
10
public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> targetDataSources) {
// 设置数据源集合
super.setTargetDataSources(targetDataSources);
super.setDefaultTargetDataSource(defaultDataSource);
}
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceKey();
}

这里只做了两件事定义数据源,以及确认当前数据源的key

定义一个Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class PomeloDataSourceConfiguration {

@Resource
private DataSourceProperties dataSourceProperties;
private DataSource defaultDataSource() {
DataSource dataSource = DataSourceBuilder.create()
.url(dataSourceProperties.getUrl())
.username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword())
.driverClassName(dataSourceProperties.getDriverClassName())
.build();;
return dataSource;
}
@Bean
@Primary
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource(defaultDataSource(), new HashMap<>());
return dynamicDataSource;
}

如果我们需要使用多个数据源那需要我们获取到多个数据源,我们这里以数据库获取为例

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
@Configuration
public class DatasourceConfig {

@Resource
private GenDatabaseService genDatabaseService;
@Resource
private DynamicDataSource dynamicDataSource;

@Bean
public DynamicDataSource dataSourceInit() {
// 创建数据源
Map<Object, Object> targetDataSources = new HashMap<>();
List<GenDatabase> genDatabaseList = genDatabaseService.selectList();
for (GenDatabase genDatabase : genDatabaseList) {
targetDataSources.put(genDatabase.getDatabaseId().toString(), DataSourceBuilder.create()
.driverClassName(genDatabase.getDatabaseDriver())
.url(genDatabase.getDatabaseUrl())
.password(genDatabase.getDatabasePwd())
.username(genDatabase.getDatabaseName()).build());
}
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.afterPropertiesSet();
return dynamicDataSource;
}
}

这时如果需要再不同的地方调用数据源可以根据 DataSourceContextHolder.setDataSourceKey来确认使用哪个数据源,如果有需要也可以以aop形式完成,这里不多赘述

1
2
DataSourceContextHolder.setDataSourceKey(tableQuery.getDatabaseId());
DataSourceContextHolder.clearDataSourceKey();