【排查了一天的坑】Flowable 监听器切换数据源失败(附详细排查过程)
有意思的是:其他接口调用 @DS(“XX”) MethodA 数据源正常切换,只有 Flowable 监听器的方法不可以。
监听器的调用本身仍处于 Flowable 的 CommandContext 中Flowable 在执行任务事件时,会打开自己的:自己的事务自己的 SqlSession自己的数据源绑定最终导致:✅ DynamicDatasource 的 Spring 代理正在起作用但❌ Flowable 的 MyBatis 环境主动覆盖了 Connection,导致 DB 仍然指向默认数据源因此,Flowable 监听器内部的 SQL 调用,多半还是走 流程主数据源
解决方案:
让处理逻辑完全“脱离 Flowable 线程1. Flowable 监听器只发送事件:
@Autowired
private ApplicationEventPublisher publisher;@Override
protected void taskCreated(FlowableEngineEntityEvent event) {TaskEntity task = (TaskEntity) event.getEntity();publisher.publishEvent(new TaskCreatedEvent(task));}
2.手动开启一个新的 Spring 事务(脱离 Flowable)
@Transactional(propagation = Propagation.REQUIRES_NEW)
@DS("usercenter")
public void MethodA (TaskEntity task) {...
}
监听器调用这个方法时,会:
暂停 Flowable 的事务;
开一个新的、纯 Spring 的事务;
动态数据源切换立即生效
在 Flowable 监听器里调用时,情况完全不同监听器是由 Flowable 引擎触发的。
当 Flowable 执行一个任务节点时,它内部是这么做的:CommandExecutor.execute(new AddTaskCmd(...))在执行过程中,它会:打开 Flowable 自己的事务使用 Flowable 的 MyBatis 环境 (DbSqlSessionFactory)从它自己保存的 DataSource 拿连接(通常是默认库)在这个环境中调用所有的 Listener于是,当你在监听器里调用 Service 时:✅ 你的 @DS 注解确实切换了 ThreadLocal;✅ DynamicRoutingDataSource 也能返回 "usercenter";❌ 但这时候 Flowable 已经提前打开了自己的 SqlSession 和 Connection;❌ 你的 MyBatis 调用虽然走 Spring,但当前线程已经绑定了 Flowable 的数据库连接上下文;❌ 所以 SQL 仍然在 Flowable 的默认库执行。换句话说:Spring 想用 "usercenter" 的 DataSource,
但 Flowable 的事务环境已经劫持了 Connection,Spring 拿到的 Connection 实际仍然来自默认库
排查过程
1.是否开启事务
2.是否本类调用 (非代理对象,aop不生效)
3.是否 private 修饰方法 (aop不生效)
4.手动切换数据源
5.打印数据源是否切换
// 在代码中直接打印当前数据源名称
String currentDataSource = DynamicDataSourceContextHolder.peek();
log.info("当前数据源:{}", currentDataSource);
6.@DS加在 Mapper、Service 都尝试了所有问题都排除了,实在没想到 flowable 监听器底层开启了自己的事务
