You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制AbstractRoutingDataSource切换数据源?

问题解决思路

你的问题核心在于private方法上的@Transactional注解不会生效。Spring的声明式事务默认基于动态代理实现,private方法无法被Spring的AOP代理捕获,所以@Transactional(TxType.REQUIRES_NEW)根本没起作用,两次查询还是处于同一个事务中,数据源不会重新路由。

可行解决方案

方案1:将方法提取到独立Service类(推荐)

把countCompanies和countUsers提取到单独的Service类中,改成public方法,让Spring能生成代理类,确保事务注解生效:

@Service
public class DataCountService {
    private final CompanyRepository companyRepository;
    private final UserRepository userRepository;

    public DataCountService(CompanyRepository companyRepository, UserRepository userRepository) {
        this.companyRepository = companyRepository;
        this.userRepository = userRepository;
    }

    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public Long countCompanies() {
        return companyRepository.count();
    }

    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public Long countUsers() {
        return userRepository.count();
    }
}

然后在Controller中注入这个Service:

@RestController
public class Controller {
    private final DataCountService dataCountService;

    public Controller(DataCountService dataCountService) {
        this.dataCountService = dataCountService;
    }

    @GetMapping("/")
    public String test(){
        String value = "";
        MultitenantContext.setCurrentTenant(Schema.company.name());
        value += dataCountService.countCompanies();

        MultitenantContext.setCurrentTenant(Schema.user.name());
        value += dataCountService.countUsers();
        return value;
    }
}

方案2:使用编程式事务管理

如果不想拆分Service,可以手动用TransactionTemplate控制事务,确保每次查询都在独立事务中执行:

@RestController
public class Controller {
    private final CompanyRepository companyRepository;
    private final UserRepository userRepository;
    private final TransactionTemplate transactionTemplate;

    public Controller(CompanyRepository companyRepository, UserRepository userRepository, TransactionTemplate transactionTemplate) {
        this.companyRepository = companyRepository;
        this.userRepository = userRepository;
        this.transactionTemplate = transactionTemplate;
    }

    @GetMapping("/")
    public String test(){
        String value = "";
        
        // 公司数据查询,独立事务
        MultitenantContext.setCurrentTenant(Schema.company.name());
        Long companyCount = transactionTemplate.execute(status -> companyRepository.count());
        value += companyCount;

        // 用户数据查询,独立事务
        MultitenantContext.setCurrentTenant(Schema.user.name());
        Long userCount = transactionTemplate.execute(status -> userRepository.count());
        value += userCount;
        
        return value;
    }
}

补充说明

AbstractRoutingDataSource的数据源路由发生在获取Connection的时刻,而Spring事务会在事务开始时获取Connection并复用它。只有当新的事务开启时,才会重新从DataSource获取Connection,此时才会触发新的路由逻辑。所以必须保证两次查询处于不同的事务中,而private方法的事务注解无法实现这一点,这就是问题的关键。

内容的提问来源于stack exchange,提问作者Fabricio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 22:50:45