如何强制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
相关产品推荐
相关产品推荐

