Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用实验性功能切换数据源,在单数据源场景中使用事务注解失败 #83

Closed
anic opened this issue Aug 1, 2019 · 7 comments

Comments

@anic
Copy link

anic commented Aug 1, 2019

Enviroment

JDK Version(required): 1.8

SpringBoot Version(required): 2.1.4.RELEASE

Starter Version(required): dynamic-datasource-spring-boot-starter 2.5.5

Describe

使用了实验性功能根据包(package)名为service选定一个数据源。

如果不增加@transactional注解,service中的方法可以正确获得数据源执行mapper方法。如果增加@transactional注解(在同一个数据源内)开启事务,则会使用primary数据源,不使用上述规则提供的数据源。

如果在service上加上@ds注解,则可以正确切换

Expected Result:
希望同一个数据源内可以通过@transactional开启注解,而不用@ds注解,毕竟已经根据包来隔离数据源了。

Actual Result:

Steps to reproduce

  • Step 1 定义配置
    @configuration
    public class DynamicConfiguration {

    @value("${demo.modules:}")
    String[] modules;

    @bean
    public DynamicDataSourceConfigure dynamicDataSourceConfigure() {
    String prefix = "com.xxx.demo";

      String surfix = ".service.impl..*"; //
      DynamicDataSourceConfigure config = DynamicDataSourceConfigure.config();
      // 对每个模块的服务增加匹配,使用该模块的数据源
      for (String m : modules) {
          config.regexMatchers(prefix + m + surfix, m);
          config.expressionMatchers("execution(* " + prefix + m + surfix + ".*(..))", m);
      }
    return config;
    

}

  • Step 2
    在com.xxx.demo.aaa.service.impl.MyService中不加@ds注解,可以使用aaa这个数据源。但一旦使用了@transactional注解,就切换到默认primary数据源中
@anic
Copy link
Author

anic commented Aug 1, 2019

这里两行是指两种都试过,实际只用一行
config.regexMatchers(prefix + m + surfix, m);
config.expressionMatchers("execution(* " + prefix + m + surfix + ".*(..))", m)

@huayanYu
Copy link
Member

huayanYu commented Aug 1, 2019

玩的高级啊....
空了看看

@anic
Copy link
Author

anic commented Aug 1, 2019

初步分析,是加了@transactional以后(没加@ds),Spring的TransactionInterceptor拦截器早于本项目的DynamicDataSourceAdvisor构建的Pointcut。

如果加了@ds,其拦截器DynamicDataSourceAnnotationInterceptor能早于TransactionInterceptor,因此就能正确切换数据源。

问题在于如何构建早于TransactionInterceptor的Pointcut?

@huayanYu
Copy link
Member

huayanYu commented Aug 1, 2019

@anic DynamicDataSourceAdvisor 设置了order,但是似乎没有生效. 你可以调试看看. 希望你解决了PR上来.

@anic
Copy link
Author

anic commented Aug 1, 2019

初步解决了:
调试发现PointCut的顺序取决于Advisor的顺序,在DynamicDataSourceAutoConfiguration的配置中
DynamicDataSourceAnnotationAdvisor是设置了顺序的

 @Bean
    @ConditionalOnMissingBean
    public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
        DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
        interceptor.setDsProcessor(dsProcessor);
        DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
        advisor.setOrder(properties.getOrder());
        return advisor;
    }

而DynamicDataSourceAdvisor则没有设置

@Bean
    @ConditionalOnBean(DynamicDataSourceConfigure.class)
    public DynamicDataSourceAdvisor dynamicAdvisor(DynamicDataSourceConfigure dynamicDataSourceConfigure, DsProcessor dsProcessor) {
        DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(dynamicDataSourceConfigure.getMatchers());
        advisor.setDsProcessor(dsProcessor);
        return advisor;
    }

解决方法:
方法1 ,下载源码,修改DynamicDataSourceAutoConfiguration在构建DynamicDataSourceAdvisor时设置Order,并重新编译打包(官方可以这样修复)

DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(dynamicDataSourceConfigure.getMatchers());
advisor.setDsProcessor(dsProcessor);
advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);

方法2,自己构建DynamicDataSourceAdvisor,并且不要构建DynamicDataSourceConfigure,这样官方的AutoConfiguration就不会构建DynamicDataSourceAdvisor而是用自己构建的那个Bean。

@Configuration
public class DynamicConfiguration {


    @Bean
    public DynamicDataSourceAdvisor dynamicAdvisor(DsProcessor dsProcessor) {

        // 参考:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/pages
        DynamicDataSourceConfigure config = DynamicDataSourceConfigure.config();
        ...

        // 直接构建DynamicDataSourceAdvisor,并设置优先级最高,也就是最外围(比TransactionInterceptor和DynamicDataSourceAnnotationInterceptor都高)
        DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(config.getMatchers());
        advisor.setDsProcessor(dsProcessor);
        advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return advisor;
    }
}

@huayanYu
Copy link
Member

huayanYu commented Aug 2, 2019

点赞

@phoema
Copy link

phoema commented Feb 23, 2020

我感觉3.0.0这个问题仍然存在啊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants