@Transactional导致 dynamic-datasource-spring-boot-starter失效原因分析

环境:controller -->Aservice–>Bservice–>Bdao(A表示A数据源,B表示B数据源)
Aservcie使用Transactional注解

1、dynamic-datasource@DB切面是可以将数据源信息push到DynamicDataSourceContextHolder类的ThreadLocal<Deque> LOOKUP_KEY_HOLDER中

2、如果controller调用service,且controller -->Aservice–>Bservice–>Bdao(A表示A数据源,B表示B数据源),当且仅当Aservice使用@Transactional进行事物管理时,当controller -->Aservice,此时AbstractPlatformTransactionManager 类调用doBegin(transaction, definition)方法,将执行TransactionSynchronizationManager的ThreadLocal<Map<Object, Object>> resources 加入A数据源

3、AbstractPlatformTransactionManager 类获取事物时候将会判断事物是否包含数据源,如果有则认为该事物已经存在,将进行事物的类型判断,如果是PROPAGATION_REQUIRES_NEW类型,将创建新事物,新事物将拥有新的数据源持有Holder,默认的话则继承事物

4、Aservice–>Bservcie虽然可以实现1中所述,但是Bservcie–>Bdao时,DataSourceUtils执行doGetConnection时候,将会执行TransactionSynchronizationManager.getResource(dataSource)获取事物数据源A(源于2的结论),此时多数剧源形同虚设,失效,

方案:
1、Bservcie使用PROPAGATION_REQUIRES_NEW 类型事物,创建新事物 原因为上述3
2、使用Aservcie 的@Transactional替换成@DSTransactional注解即可,原因如下:
dynamic-datasource-spring-boot-starter启动时,进行DynamicDataSourceAutoConfiguration初始化,aop自定义事物,此时不走spring事物规则,将使用自定义事物,这个看起来很简单的事物规则,规避了复杂业务逻辑下的不便,代码如下:

@Slf4j
public class DynamicLocalTransactionAdvisor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        if (!StringUtils.isEmpty(TransactionContext.getXID())) {
            return methodInvocation.proceed();
        }
        boolean state = true;
        Object o;
        String xid = UUID.randomUUID().toString();
        TransactionContext.bind(xid);
        try {
            o = methodInvocation.proceed();
        } catch (Exception e) {
            state = false;
            throw e;
        } finally {
            ConnectionFactory.notify(state);
            TransactionContext.remove();
        }
        return o;
    }
}