文章

多数据源管理

多数据源管理

框架已具备配置多数据源和动态切换数据源的能力。


1.表设计

数据源表的设计如下:

27-1

code是每个数据源的代码,可以用来唯一标识数据源,因此code不能重复。

示例代码如下:

27-2


2.修改配置

2.1.数据库配置

未使用动态数据源的数据库配置如下

1
2
3
4
5
6
#----------------数据库配置------------------------------------------------
spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver
spring.datasource.url=jdbc:dm://xxx:xxx/BACKEND_FRAMEWORK_TEMPLATE?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&schema=BACKEND_FRAMEWORK_TEMPLATE
spring.datasource.username=BACKEND_FRAMEWORK_TEMPLATE
spring.datasource.password=BACKEND_FRAMEWORK_TEMPLATE
spring.datasource.configuration.maximum-pool-size=100

使用动态数据源,首先需要修改数据库配置,在对应环境的framework配置文件中删掉原有的数据源连接信息,改成如下

1
2
3
4
5
6
#----------------动态数据源默认数据库配置-------------------------------------
spring.datasource.default-db.driver-class-name=dm.jdbc.driver.DmDriver
spring.datasource.default-db.jdbc-url=jdbc:dm://xxx:xxx/BACKEND_FRAMEWORK_TEMPLATE?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8&schema=BACKEND_FRAMEWORK_TEMPLATE
spring.datasource.default-db.username=BACKEND_FRAMEWORK_TEMPLATE
spring.datasource.default-db.password=BACKEND_FRAMEWORK_TEMPLATE
spring.datasource.default-db.configuration.maximum-pool-size=100

2.2.动态数据源启动配置

在对应环境的utility配置文件中添加如下配置

1
2
#-----------------------------动态数据源----------------------------------
base.utility.datasource.enable=true

2.3.启动类上添加动态数据源注解

去掉改原有的@SpringBootApplication注解,改成@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// @SpringBootApplication
/** 去掉数据源自动注册,来使用动态数据源功能 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
/** 启用缓存 */
@EnableCaching
/** 扫描mybatis mapper */
@MapperScan("**.mapper")
/** 启用Spring Security */
@EnableWebSecurity
/** 启用Spring Security方法权限 */
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}


3.数据源的增删查改接口

直接调用接口即可,新增和修改的数据源会保存在内存中。

27-3


4.动态数据源的使用

在使用动态数据源前需要先通过add接口添加数据源,否则系统中只会有一个code为defaultDB的默认数据源,该数据源为配置文件中的数据源

4.1.指定数据源

如果需要使用的数据源是明确的,可以指定数据源,在需要指定数据源的方法上加上注解@DynamicDS并指定数据源的代码code。

例如先用add接口添加一个code为MysqlDb的数据源,在指定使用MysqlDb数据源时demo如下:

1
2
3
4
5
6
@DynamicDS("MysqlDb")
@Operation(description = "测试指定数据源")
@GetMapping(value = "/test")
public List<Map<String, Object>> testSql() {
  return myBaseService.testSql();
}

4.2.多数据源切换

可以由前端传递code,在controller层根据code进行数据源动态切换。动态数据源需要在方法加上注解@DynamicDS,并且保证方法的第一个参数是数据源代码code。

本例是查询该数据源下所有的模式:

1
2
3
4
5
6
@DynamicDS
@Operation(description = "获取模式")
@RequestMapping(value = "/getSchemaList/{code}", method = RequestMethod.GET)
public List<String> getSchemaList(@PathVariable String code) {
  return myBaseService.getSchemaList(code);
}

如果在同一个service需要调用切换数据源的方法时不能直接调用,需要先获取当前service的bean,再调用的bean的切换数据源方法,这样才能进入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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * 检查数据源状态 此处需要切换数据源
 *
 * @param code
 * @return
 */
@Override
@DynamicDS
public String checkStatus(String code) {
  try {
    // 检查数据源状态
    List<String> result = baseMapper.checkStatus(code);
    if (result != null && result.size() > 0) {
      return "success";
    }
  } catch (Exception e) {
    log.error("测试连接出现异常:" + e.getCause().getCause().getLocalizedMessage());
    // 释放数据源
    releaseDs(code);
    return "fail";
  }
  return "fail";
}
/**
 * 更新数据源连接状态,同时会更新启用停用状态
 *
 * <p>如果原本是启用状态,但是数据源连接失败,则更新状态为停用
 *
 * <p>如果原本是停用用状态,无论数据源连接成功与否,都不改变停用状态
 *
 * @param myDatasource
 */
@Override
public String updateDatasourceStatus(MyDatasource myDatasource) {
  // 采用这种方法,可以使得checkStatus被容器管理从而进入切面
  MyDatasourceServiceImpl datasourceService =
      SpringBeanUtils.getBean(MyDatasourceServiceImpl.class);
  // 检查数据源状态
  String checkStatus = "fail";
  try {
    // 在检查数据源时如果数据源不存在会创建数据源,会出现数据源创建失败报错的情况,故在此捕获异常
    checkStatus = datasourceService.checkStatus(myDatasource.getCode());
  } catch (BusinessException e) {
    log.error(e.getMessage());
  }
  myDatasource.setCheckStatus(checkStatus);
  myDatasource.setCheckStatusTime(new Date());
  // 根据连接状态设置启用还是停用,如果原数据中的数据源就是停用状态,则不主动启用
  if ("success".equals(checkStatus) && !"disable".equals(myDatasource.getStatus())) {
    myDatasource.setStatus("active");
  } else {
    myDatasource.setStatus("disable");
  }
  // 更新数据源
  baseMapper.updateById(myDatasource);
  return checkStatus;
}


常见问题

  1. Invalid bound statement (not found)错误

mybatis的xml文件需要放在resource下的mapper文件夹下。

因为框架使用的mapperLocations路径默认为:classpath:mapper/.xml。

也可以通过以下参数修改mapperLocations值:(需要开发框架版本>=1.2.0) mybatis-plus.mapper-locations=xxx

本文由作者按照 CC BY 4.0 进行授权