springboot集成flyway使用多数据源的一种实现方式

项目中使用flyway做数据库的版本管理,近期整理项目需要将原本两个包的代码重构合并成一个包,但各自的数据源保留不整合,所以需要flyway实现多数据源迁移,项目中的数据库操作(jpa)同样也需要实现多数据源切换。
这个实现方式会修改flyway执行的时间节点,各位请按照自己的需求,选择性参考。
实现思路:
flyway:读取配置文件里的多个数据源配置,使用这些数据源配置构建Flyway对象,并且在代码里通过调用flyway.migrate()方法通知flyway对象去执行迁移。
jpa:依赖dynamic-datasource-spring-boot-starter之后,在配置文件中配置好数据源,在对应的Repository接口上使用@DC注解即可,其参数为配置文件中数据源的key。该注解可以用在类或方法上,且遵循就近原则,既接口和接口中的方法都使用了该注解,方法调用时以方法上的注解配置为准。
maven依赖:

		
		
            com.baomidou
            dynamic-datasource-spring-boot-starter
            3.4.1
        
        
        
            org.flywaydb
            flyway-core
            5.2.1
        

配置文件:
1.依赖dynamic-datasource-spring-boot-starter之后,数据源要配置在datasource.dynaamic.datasource下。
2.flyway的配置里enabled配置要设置成false,关闭自动执行。本人对flyway并不熟悉,没过多的研究过源码和文档,仅仅是会用的阶段。观察到项目启动时flyway的自动执行和spring ioc的初始化是同步执行的,如果不关掉自动执行的话,flyway的自动执行会先在主数据源下把配置路径下的迁移全部执行一次,等spring ioc把配置类实例化出来之后,触发执行的才是需要的多数据源迁移,并且不保证两次迁移不起冲突。关掉自动执行只保留触发执行就能解决这个问题,具体问题并没有深究,在自动执行中使用ioc容器的对象或者配置,不是本文的讨论范围,故此没有深究这个问题。

spring:
  datasource:
    dynamic:
      datasource:
        db1: #可以通过这个key获取对应的数据源
          driverClassName: com.mysql.cj.jdbc.Driver
          url: 
          username: 
          password: 
        db2:
          driverClassName: com.mysql.cj.jdbc.Driver
          url: 
          username: 
          password: 
        db3:
          driverClassName: com.mysql.cj.jdbc.Driver
          url: 
          username: 
          password: 
      primary: db1 #主数据源
      strict: true
  jpa:
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
      ddl-auto: none
      cache:
        use_second_level_cache: true
        use_query_cache: true
        region:
          factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL55Dialect
  flyway:
    enabled: false #上文提到这个实现会改变flyway的执行时间节点,所以这里的enabled要配置成false,关掉flyway的自动执行,将flyway的执行时机改为在代码中触发
    baseline-on-migrate: true
    baseline-description: dynamic baseLine
    out-of-order: true
    validate-on-migrate: false
    locations: classpath:/db/migration, com/db/migration

我的项目中使用了两种迁移方式,classpath:/db/migration放的是sql脚本文件,com/db/migration包里放的是api方式的迁移。之前看过几个flyway多数据源实现的文章,大部分的locations配置的都是一个路径,对多个路径配置的方式并没有做过多解释,最开始我根据大佬们的操作仿写的配置类就因为多个路径踩了个坑:同一个数据源构建了两个不同location的Flyway对象,并且分别触发迁移。这就导致sql脚本和java api分离开来单独执行了,但实际版本号是交叉在这两种类型的迁移,这就导致一个路径下的迁移执行成功后,另一个路径下的迁移版本校验无法通过的问题。

摘抄一位大佬提到的另一个坑:为什么网上的很多方案都不行的原因就是,如果这里的localtions配置为db或者db/migration这样存在所有flyway版本sql的路径,则最终结果会导致项目跑起来,flysqy会在主库中出现所有其他从库的表和自己主库本该有的表。原文在这里
综合这两个坑的解决方案,我这里只配置了两个路径的公共部分,不同数据源不同的文件夹在配置类里动态拼接上

文件路径结构:
springboot集成flyway使用多数据源的一种实现方式_第1张图片

修改过后的配置类:

public class FlywayConfig {
    private final DataSource dataSource;

    @Value("${spring.flyway.locations}")
    private String SQL_LOCATION;

    @Value("${spring.flyway.baseline-on-migrate}")
    private boolean BASELINE_ON_MIGRATE;
    
    @Bean
    @PostConstruct
    public void migrateOrder() {
        List locations = Arrays.stream(SQL_LOCATION.split(",")).collect(Collectors.toList());
        // 根据数据源分别去构建属于这个数据源的locations的路径,用这个map保存,key:数据源名称。value:迁移脚本所在的路径
        Map> flywayLocals = new HashMap<>();
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        Map dataSources = ds.getDataSources();
        locations.forEach(location -> {
            String finalLocation = location;
            dataSources.forEach((k, v) -> {
                String s = finalLocation + "/" + k;
                List flyways = flywayLocals.getOrDefault(k, new ArrayList<>());
                flyways.add(s);
                flywayLocals.put(k,flyways);
            });
        });
        //真正的触发迁移执行在这个循环里
        flywayLocals.forEach((k,v)->{
            Flyway flyway = Flyway.configure()
                    .dataSource(dataSources.get(k))
                    .locations(v.toArray(new String[0]))
                    .baselineOnMigrate(BASELINE_ON_MIGRATE)
                    .load();
            flyway.migrate();
        });
    }
}

启动项目,flyway会在spring ioc完成对FlywayConfig 的实例化时立即执行。相比于flyway的自动执行,执行的时间略微后置了一下。后置这段时间对我的项目是没有影响的,各位参考时请根据自己项目的需求选择性使用

你可能感兴趣的:(学习记录,spring,boot,spring,java,后端)