遇到的问题之“加入Spring的事务  @Transactional(rollbackFor = Exception.class)导致@DS数据库切换失效”

遇到的问题之“加入Spring的事务  @Transactional(rollbackFor = Exception.class)导致@DS数据库切换失效”

1. 核心问题:事务与数据源的绑定时机

@Service
public class DataService {@DS("starrocks")  // 希望切换到 StarRocks@Transactional     // Spring 事务注解public void processData() {// 方法体
    }
}

 

执行流程问题:

  1. 事务切面先执行:Spring 的 @Transactional 切面先执行

  2. 获取数据库连接:事务管理器从默认数据源获取连接

  3. 开启事务:在默认连接上开启事务

  4. DS切面后执行:@DS 注解的切面后执行,尝试切换数据源

  5. 数据源切换失败:此时连接已绑定到事务,无法再切换

2. 代码层面的详细分析

// 伪代码:AOP 执行顺序
public Object invoke(MethodInvocation invocation) {// 1. 事务切面先执行
    TransactionAspectSupport.beginTransaction();//    ↓ 这里获取了默认数据源的连接// 2. DS切面后执行(如果切面顺序没调整)DynamicDataSourceContextHolder.push("starrocks");// 3. 执行业务方法Object result = invocation.proceed();// 4. DS切面清理
    DynamicDataSourceContextHolder.poll();// 5. 事务切面提交
    TransactionAspectSupport.commitTransaction();
}

 

3. 具体表现场景

场景1:嵌套服务调用

@Service
public class UserService {@DS("mysql")  // 主库
    @Transactionalpublic void updateUser(User user) {userMapper.update(user);// 调用统计服务statsService.updateStats(user);  // 这里可能无法切换到 StarRocks
    }
}@Service  
public class StatsService {@DS("starrocks")  // 希望用 StarRocks@Transactional(propagation = Propagation.REQUIRES_NEW)  // 新事务public void updateStats(User user) {// 由于外层已有事务,这里可能仍然使用 MySQL 连接
        statsMapper.insert(user);}
}