springboot利用AbstractRoutingDat

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

动态切换数据源:

springboot提供了一个AbstractRoutingDataSource类。我们可以实现一个类继承AbstractRoutingDataSource并且determineCurrentLookUpKey()方法。

具体步骤:

数据源配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
yaml复制代码spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#MySQL配置
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/graduate?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
#MySQL配置
dbmanager:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/hfb?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
db37:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sys?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
dbmanager37:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/srb_core?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root

编写 DataSourceContextHolder设置和保存当前线程使用的数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typescript复制代码public class DataSourceContextHolder {

//默认数据源
private static final String DEFAULT_DATASOURCE = "pq";

//保存线程连接的数据源
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

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

public static void setDataSource(String key) {
CONTEXT_HOLDER.set(key);
}

public static void cleanDataSource() {
CONTEXT_HOLDER.remove();
}
}

编写DynamicDataSource 

1
2
3
4
5
6
7
8
9
10
11
12
scala复制代码public class DynamicDataSource extends AbstractRoutingDataSource {

/**
* @return 切换数据源的时候该方法会被调用
*/
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}


}

配置类

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
less复制代码/**
* 多数据源配置类
*/
@Configuration
public class DataSourceConfig {

//@Primary
@Bean(name = "db")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSourcePq() {
return new DruidDataSource();
}

@Bean("db_37")
@ConfigurationProperties("spring.datasource.pq37")
public DataSource dataSourcePq37() {
return new DruidDataSource();
}

@Bean("db_manager_37")
@ConfigurationProperties("spring.datasource.pqmanager37")
public DataSource dataSourcePqManager37() {
return new DruidDataSource();
}

@Primary //必须有一个数据源标记为Primary
@Bean("db_manager")
@ConfigurationProperties("spring.datasource.pqmanager")
public DataSource dataSourcePqManager() {
return new DruidDataSource();
}

/**
* 数据源选择器 如果此处标记@Primary会导致循环依赖问题
*
* @return
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("db") DataSource PqSource,
@Qualifier("db_manager") DataSource PqManagerSource,
@Qualifier("db_37") DataSource PqSource37,
@Qualifier("db_manager_37") DataSource PqManagerSource37) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//配置默认数据源
dynamicDataSource.setDefaultTargetDataSource(PqSource);
//保存所有可切换的数据源
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("db", PqSource);
dataSourceMap.put("db_manager", PqManagerSource);
dataSourceMap.put("db_37", PqSource37);
dataSourceMap.put("db_manager_37", PqManagerSource37);
dynamicDataSource.setTargetDataSources(dataSourceMap);
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
java复制代码@EnableTransactionManagement
@Configuration
public class MyBatisConfig {

@Resource(name = "dynamicDataSource")
private DataSource dynamicDataSource;

@Autowired
private MybatisProperties mybatisProperties;

@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
sqlSessionFactoryBean.setMapperLocations(mybatisProperties.resolveMapperLocations());
sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration());
return sqlSessionFactoryBean.getObject();
}

@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dynamicDataSource);
}

}

利用拦截器拦截请求,查看请求参数是否存在某种参数(代表需要切换数据源,不存在则为默认数据源)。也可以使用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
31
32
33
34
35
36
37
java复制代码/**
* 拦截请求切换数据源
*/
public class DataSourceInterceptor extends HandlerInterceptorAdapter {

/**
* 拦截请求
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String dataSourceKey = request.getParameter("dataSourceKey");
System.out.println(dataSourceKey);
if (StringUtils.isNotEmpty(dataSourceKey)) {
DataSourceContextHolder.setDataSource(dataSourceKey);//重点
}
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {


// System.out.println(DataSourceContextHolder.getDataSource());
DataSourceContextHolder.cleanDataSource();
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
}

配置拦截器

1
2
3
4
5
6
7
8
9
java复制代码@Configuration
public class InterceptorsConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(new DataSourceInterceptor());
//拦截所有路径
registration.addPathPatterns("/sys/**");
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%