Spring Boot Flyway 将数据库迁移应用到错误的数据库

Posted

技术标签:

【中文标题】Spring Boot Flyway 将数据库迁移应用到错误的数据库【英文标题】:Spring Boot Flyway applying database migration to wrong database 【发布时间】:2020-10-17 22:21:16 【问题描述】:

我正在尝试配置 Spring Boot 和 Flyway 以使用 Spring Boot 2.2.6 和 Flyway 5.2.3 将单独的迁移应用到两个不同的数据源。

主要数据源:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.gobsmack.*.repository.primary",
                       entityManagerFactoryRef = "primaryEntityManagerFactory",
                       transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDatasourceConfiguration 

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.primary-datasource")
    public DataSource primaryDataSource() 
        return DataSourceBuilder.create().build();
    

    @Primary
    @Bean(name = "primaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) 
        return builder.dataSource(primaryDataSource())
                .packages("com.gobsmack.*.model")
                .build();
    

    @Primary
    @Bean
    public PlatformTransactionManager primaryTransactionManager(
            final @Qualifier("primaryEntityManagerFactory") LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory) 
        return new JpaTransactionManager(primaryEntityManagerFactory.getObject());
    

第二个数据源:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.gobsmack.*.repository.secondary",
                       entityManagerFactoryRef = "secondaryEntityManagerFactory",
                       transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDatasourceConfiguration 

    @Bean
    @ConfigurationProperties(prefix = "spring.secondary-datasource")
    public DataSource secondaryDataSource() 
        return DataSourceBuilder.create().build();
    

    @Bean(name = "secondaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) 
        return builder.dataSource(secondaryDataSource())
                .packages("com.gobsmack.*.model")
                .build();
    

    @Bean
    public PlatformTransactionManager secondaryTransactionManager(
            final @NonNull @Qualifier("secondaryEntityManagerFactory") LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory) 
        return new JpaTransactionManager(secondaryEntityManagerFactory.getObject());
    

数据源连接属性:

spring:
  primary-datasource:
    jdbc-url: jdbc:mysql://localhost:3306/primary?useSSL=false&primary
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: username
  secondary-datasource:
    jdbc-url: jdbc:mysql://localhost:3306/secondary?useSSL=false&secondary
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: username

Flyway 迁移:

@Slf4j
@Configuration
public class FlywayConfiguration 

    public static final String DB_MIGRATION_BASE = "db/migration/";

    private DataSource primaryDataSource;
    private DataSource secondaryDataSource;

    @Value("$spring.primary-datasource.jdbc-url:localhost")
    private String primaryDatasourceUrl;

    @Value("$spring.secondary-datasource.jdbc-url:localhost")
    private String secondaryDatasourceUrl;

    @Autowired
    public FlywayConfiguration(@Qualifier("primaryDataSource") DataSource primaryDataSource,
                               @Qualifier("secondaryDataSource") DataSource secondaryDataSource) 
        this.primaryDataSource = primaryDataSource;
        this.secondaryDataSource = secondaryDataSource;
    

    @PostConstruct
    public void migrate() 
        log.info("Preparing Flyway migration for database with url: " + primaryDatasourceUrl);
        migrateDatabase(primaryDataSource, "primary");

        log.info("Preparing Flyway migration for database with url: " + secondaryDatasourceUrl);
        migrateDatabase(secondaryDataSource, "secondary");
    

    /**
     * Migrate specific database.
     * @param datasource The datasource.
     * @param directories The directories containing the migration scripts.
     */
    private void migrateDatabase(@NonNull DataSource datasource, @NonNull String... directories) 
        for (val directory : directories) 
            log.info("Migration scripts source: " + directory);
            new Flyway(Flyway.configure()
                             .dataSource(datasource)
                             .locations(DB_MIGRATION_BASE + directory))
                             .migrate();
        
    

目录结构:

迁移脚本V1.0.0_1__secondary__init.sql 仅正确应用于secondary 数据库。

问题是db/migration/primarydb/migration/secondary 中的脚本都被应用到primary 数据库,而应该只应用主目录中的脚本。

如何将 Flyway 配置为仅将目录 db/migration/primary 中的迁移脚本应用到 primary 数据库?

【问题讨论】:

【参考方案1】:

我不得不禁用 Flyway 的自动运行,它正在查找脚本以应用于 @Primary 带注释的数据库。它在db/migration 的子目录中查找所有迁移脚本。

所以我必须在application.yml 配置文件中添加:

spring:
  flyway:
    enabled: false

【讨论】:

以上是关于Spring Boot Flyway 将数据库迁移应用到错误的数据库的主要内容,如果未能解决你的问题,请参考以下文章

使用 Flyway 和 Spring Boot 迁移基线

使用 Spring Boot 进行 Flyway 修复

Spring Boot 应用程序在启动时未运行 Flyway 迁移

在 Spring Boot 应用程序中使用 Flyway 进行多数据源迁移

使用 Flyway 和 Spring Boot 迁移具有不同生命周期的多个模式

Flyway 与 Spring Boot 的集成不会在嵌入式 H2 数据库上执行迁移脚本