使用dynamic-datasource-spring-boot-starter,通过接口实现动态切换数据源
使用dynamic-datasource-spring-boot-starter,通过接口实现动态切换数据源
·
以下是 通过接口动态切换数据源 的完整实现流程,基于 dynamic-datasource-spring-boot-starter 4.2.0,确保切换后所有接口使用新数据源。
1. 添加依赖
确保 pom.xml 包含以下依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
2. 配置数据源
在 application.yml 中配置多数据源:
spring:
datasource:
dynamic:
primary: master # 默认数据源
strict: false # 是否严格匹配数据源
datasource:
master:
url: jdbc:mysql://192.168.0.4:3306/qy2?useSSL=false&serverTimezone=Asia/Shanghai
username: mysql
password: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
slave1:
url: jdbc:mysql://127.0.0.1:3306/qy2?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
3. 实现全局数据源拦截器
在请求前根据 Session 中的标识切换数据源:
@Component
@Slf4j
public class GlobalDataSourceInterceptor implements HandlerInterceptor {
@Resource
private DynamicDataSourceProperties dataSourceProperties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession(false);
log.warn("当前 Session ID: " + (session != null ? session.getId() : "null"));
if (session != null) {
String ds = (String) session.getAttribute("current_ds");
log.warn("从 Session 中读取的数据源: {}",ds);
Set<String> dataSourceNames = dataSourceProperties.getDatasource().keySet();
if (ds != null && dataSourceNames.contains(ds)) {
DynamicDataSourceContextHolder.push(ds);
log.warn("切换数据源: {}",ds);
}else {
DynamicDataSourceContextHolder.push("master");
log.warn("切换到默认数据源: master");
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
DynamicDataSourceContextHolder.clear();
}
}
注册拦截器并设置最高优先级:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DataSourceInterceptor dataSourceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(dataSourceInterceptor)
.order(Ordered.HIGHEST_PRECEDENCE); // 确保最先执行
}
}
4. 编写切换接口
通过接口将数据源名称保存到 Session 中:
@RestController
@Api(tags = "数据源管理")
public class DataSourceController {
@Autowired
private DynamicDataSourceProperties dataSourceProperties;
@PostMapping("/switch")
@ApiOperation("切换数据源")
public ResponseEntity<String> switchDataSource(
@RequestParam @ApiParam("数据源名称(如 master, slave1)") String ds,
HttpSession session
) {
// 校验数据源是否存在
Set<String> validDataSources = dataSourceProperties.getDatasource().keySet();
if (!validDataSources.contains(ds)) {
return ResponseEntity.badRequest().body("无效数据源: " + ds);
}
// 保存到 Session
session.setAttribute("current_ds", ds);
return ResponseEntity.ok("全局数据源已切换至: " + ds);
}
@GetMapping("/current")
@ApiOperation("查询当前数据源")
public ResponseEntity<String> getCurrentDataSource(HttpSession session) {
String currentDs = (String) session.getAttribute("current_ds");
if (currentDs == null) {
return ResponseEntity.ok("当前使用默认数据源: " + dataSourceProperties.getPrimary());
}
return ResponseEntity.ok("当前数据源: " + currentDs);
}
}
5. 事务处理建议
场景 1:无事务方法
直接使用拦截器切换数据源即可。
场景 2:需要事务的方法
在事务外层手动切换数据源:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getUsersFromSlave() {
// 手动切换数据源
DynamicDataSourceContextHolder.push("slave1");
return transactionalGetUsers();
}
@Transactional
public List<User> transactionalGetUsers() {
return userMapper.selectList(null);
}
}
6. 验证流程
步骤 1:切换数据源
POST http://localhost:8080/switch?ds=slave1
响应:全局数据源已切换至: slave1
步骤 2:查询当前数据源
GET http://localhost:8080/current
响应:当前数据源: slave1
步骤 3:执行业务查询
GET http://localhost:8080/api/users
观察控制台日志:
[数据源拦截器] 已切换至数据源: slave1
7. 常见问题排查
问题 1:切换无效
- 检查点:
- 确认拦截器日志是否打印切换成功。
- 检查 Session ID 是否一致(切换和查询是否同一会话)。
- 检查业务方法是否开启了事务(导致数据源提前绑定)。
问题 2:数据源不存在
- 检查点:
- 确保
application.yml中数据源名称与代码中一致。 - 校验切换接口中的
validDataSources.contains(ds)逻辑。
- 确保
问题 3:连接失败
- 检查点:
- 直接使用 MySQL 客户端连接
slave1的 URL、用户名、密码。 - 检查防火墙或网络权限。
- 直接使用 MySQL 客户端连接
8. 最终效果
- 动态切换:通过
/switch接口切换后,所有后续请求自动使用新数据源。 - 会话隔离:不同用户的 Session 独立维护数据源状态。
- 事务安全:通过外层手动切换避免事务绑定问题。
通过以上步骤,可以实现通过接口动态切换全局数据源。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)