如何从flyway 3直接升级到flyway 5

     2023-03-28     60

关键词:

【中文标题】如何从flyway 3直接升级到flyway 5【英文标题】:How to upgrade from flyway 3 directly to flyway 5 【发布时间】:2018-10-01 09:00:08 【问题描述】:

开发由许多客户在许多生产环境中部署的产品。它包括至少一个 Spring Boot 应用程序。

我们使用 flyway 进行数据库架构迁移。从 Spring Boot 1.5.x 升级到 2.0.x 将我们的 flyway 版本从 3.x 升级到 5.x。

Spring Boot 迁移指南只是说在引导升级之前升级到 flyway 4。但是,这需要我们所有的客户进行中间升级,然后才能升级到最新版本。

那么,问题是:如何从 flyway 3 直接升级到 flyway 5?

【问题讨论】:

有点讽刺和令人沮丧的是,flyway 的唯一目的是平滑 db 模式版本迁移,但无法顺利处理自己的模式版本迁移。为什么flyway不使用自己的工具来处理升级? 【参考方案1】:

如果我不是地球上最后一个仍在从 3 升级到 5 的人。

问题:

我希望升级对项目中的其他开发人员透明,并且在升级实时应用程序时不需要任何特殊的部署说明,所以我做了以下操作。

我查看了版本 4 如何处理升级:

在 Flyway.java 中调用 MetaDataTableImpl.upgradeIfNecessary upgradeIfNecessary 检查 version_rank 列是否仍然存在,如果存在,则从 org/flywaydb/core/internal/dbsupport/YOUR_DB/ 运行名为 upgradeMetaDataTable.sql 的迁移脚本 如果执行了 upgradeIfNecessary,则 Flyway.java 运行 DbRepair 调用 repairChecksumsAndDescriptions

这很容易手动完成,但要使其透明。该应用程序是一个 spring 应用程序,但不是一个 spring boot 应用程序,所以当时我通过使 LocalContainerEntityManager bean 构造依赖于 flyway bean 来在应用程序启动时自动运行迁移,这将调用 migrate 作为它的 init 方法(在这里解释 @ 987654321@),所以引导的顺序是:

Flyway bean created -> Flyway migrate called -> LocalContainerEntityManager created

解决方案:

我将引导顺序更改为:

Flyway bean created -> Flyway3To4Migrator -> LocalContainerEntityManager created

如果需要,Flyway3To4Migrator 将在其中执行 schema_table 更改,如果发生升级则运行修复,然后始终运行 flyway.migrate 以继续迁移。

@Configuration
public class AppConfiguration 

    @Bean
    // Previously: @DependsOn("flyway")
    @DependsOn("flyway3To4Migrator")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) 
        ...
    

    // Previously: @Bean(initMethod = "migrate")
    @Bean
    public Flyway flyway(DataSource dataSource) 
        ...
    


@Component
@DependsOn("flyway")
public class Flyway3To4Migrator 
    private final Log logger = LogFactory.getLog(getClass());
    private Flyway flyway;

    @Autowired
    public Flyway3To4Migrator(Flyway flyway) 
        this.flyway = flyway;
    

    @PostConstruct
    public void migrate() throws SQLException, MetaDataAccessException 
        DataSource dataSource = flyway.getDataSource();

        boolean versionRankColumnExists = checkColumnExists(dataSource);
        if (versionRankColumnExists) 
            logger.info("Upgrading metadata table to the Flyway 4.0 format ...");
            Resource resource = new ClassPathResource("upgradeMetaDataTable.sql", getClass().getClassLoader());
            ScriptUtils.executeSqlScript(dataSource.getConnection(), resource);
            logger.info("Metadata table successfully upgraded to the Flyway 4.0 format.");

            logger.info("Running flyway:repair for Flyway upgrade.");
            flyway.repair();
            logger.info("Complete flyway:repair.");
        

        logger.info("Continuing with normal Flyway migration.");
        flyway.migrate();
    

    private boolean checkColumnExists(DataSource dataSource) throws MetaDataAccessException 
        return (Boolean) JdbcUtils.extractDatabaseMetaData(
            dataSource, dbmd -> 
                ResultSet rs = dbmd.getColumns(
                        null, null,
                        "schema_version",
                        "version_rank");
                return rs.next();
            );
    

需要注意的几点:

在某些时候,我们将删除额外的 Flyway3To4Migrator 类并将配置恢复为原来的方式。 我从 v4 Flyway jar 中为我的数据库复制了相关的 upgradeMetaDataTable.sql 文件,并将其简化为我的表名等。如果需要,您可以从 flyway 中获取架构和表名。 SQL 脚本周围没有事务管理,您可能需要添加它 Flyway3To4Migrator 调用 flyway.repair(),它的作用比 DbRepair.repairChecksumsAndDescriptions() 多一点,但我们很高兴接受数据库在运行前必须处于良好状态

【讨论】:

【参考方案2】:

第 0 步。

升级到 spring boot v2.1(然后隐式升级到 flyway 5)。

第 1 步。

由于 schema_version 在 flyway 3.x 中使用,让新的 flyway 版本知道他们应该继续使用此表。:

# application.yml
spring.flyway.table: schema_version # prior flyway version used this table and we keep it

第 2 步。

创建文件src/main/ressources/db/migration/flyway_upgradeMetaDataTable_V3_to_V4.sql,用于根据您使用的方言升级元表。

几种方言的更新脚本见https://github.com/flyway/flyway/commit/cea8526d7d0a9b0ec35bffa5cb43ae08ea5849e4#diff-b9cb194749ffef15acc9969b90488d98。

这是 postgres 的一个,假设 flyway 表名称是schema_version

-- src/main/ressources/db/migration/flyway_upgradeMetaDataTable_V3_to_V4.sql
DROP INDEX "schema_version_vr_idx";
DROP INDEX "schema_version_ir_idx";
ALTER TABLE "schema_version" DROP COLUMN "version_rank";
ALTER TABLE "schema_version" DROP CONSTRAINT "schema_version_pk";
ALTER TABLE "schema_version" ALTER COLUMN "version" DROP NOT NULL;
ALTER TABLE "schema_version" ADD CONSTRAINT "schema_version_pk" PRIMARY KEY ("installed_rank");
UPDATE "schema_version" SET "type"='BASELINE' WHERE "type"='INIT';

第 3 步。

创建Java文件your.package/FlywayUpdate3To4Callback.java

请注意,它会执行以下操作:

从第 2 步运行 sql 脚本 致电Flyway.repair()
// FlywayUpdate3To4Callback.java
package your.package;

import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;

import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.callback.Context;
import org.flywaydb.core.api.callback.Event;
import org.flywaydb.core.api.configuration.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@Order(HIGHEST_PRECEDENCE)
@Slf4j
public class FlywayUpdate3To4Callback implements Callback 
    private final Flyway flyway;

    public FlywayUpdate3To4Callback(@Lazy Flyway flyway) 
        this.flyway = flyway;
    

    private boolean checkColumnExists(Configuration flywayConfiguration) throws MetaDataAccessException 
        return (boolean) JdbcUtils.extractDatabaseMetaData(flywayConfiguration.getDataSource(),
                callback -> callback
                        .getColumns(null, null, flywayConfiguration.getTable(), "version_rank")
                        .next());
    

    @Override
    public boolean supports(Event event, Context context) 
        return event == Event.BEFORE_VALIDATE;
    

    @Override
    public boolean canHandleInTransaction(Event event, Context context) 
        return false;
    

    @Override
    public void handle(Event event, Context context) 
        boolean versionRankColumnExists = false;
        try 
            versionRankColumnExists = checkColumnExists(context.getConfiguration());
         catch (MetaDataAccessException e) 
            log.error("Cannot obtain flyway metadata");
            return;
        
        if (versionRankColumnExists) 
            log.info("Upgrading metadata table the Flyway 4.0 format ...");
            Resource resource = new ClassPathResource("db/migration/common/flyway_upgradeMetaDataTable_V3_to_V4.sql",
                    Thread.currentThread().getContextClassLoader());
            ScriptUtils.executeSqlScript(context.getConnection(), resource);
            log.info("Flyway metadata table updated successfully.");
            // recalculate checksums
            flyway.repair();
        
    

第 4 步。

运行弹簧靴。

日志应显示类似于以下的信息消息:

...FlywayUpdate3To4Callback      : Upgrading metadata table the Flyway 4.0 format 
...FlywayUpdate3To4Callback      : Flyway metadata table updated successfully.

学分

此答案基于 Eduardo Rodrigues 的答案更改:

使用Event.BEFORE_VALIDATE触发将flyway 3升级到4的flyway回调。 有关 application.yml 设置的更多信息 提供升级sql迁移脚本

【讨论】:

感谢您的回答。我对 flyway 和 Spring 菜鸟完全陌生,所以我不太确定从第 3 步到第 4 步该怎么做。你能更详细地解释一下吗?我必须在哪里创建这个 Java 文件?我必须在运行应用程序之前执行 maven 吗? 这个答案是非常详细的正确解决方案,但请寻找以下与不同版本的 FlyWay 相关的答案【参考方案3】:

如果您使用的是 Spring Boot,则可以在 beforeMigrate() 上注册一个执行升级的回调。代码类似于@trf,如下所示:

@Component
@Order(HIGHEST_PRECEDENCE)
@Slf4j
public class FlywayUpdate3To4Callback extends BaseFlywayCallback 
    private final Flyway flyway;

    public FlywayUpdate3To4Callback(@Lazy Flyway flyway) 
        this.flyway = flyway;
    

    @Override
    public void beforeMigrate(Connection connection) 
        boolean versionRankColumnExists = false;
        try 
            versionRankColumnExists = checkColumnExists(flywayConfiguration);
         catch (MetaDataAccessException e) 
            log.error("Cannot obtain flyway metadata");
            return;
        
        if (versionRankColumnExists) 
            log.info("Upgrading metadata table the Flyway 4.0 format ...");
            Resource resource = new ClassPathResource("upgradeMetaDataTable.sql",
                    Thread.currentThread().getContextClassLoader());
            ScriptUtils.executeSqlScript(connection, resource);
            log.info("Flyway metadata table updated successfully.");
            // recalculate checksums
            flyway.repair();
        
    

    private boolean checkColumnExists(FlywayConfiguration flywayConfiguration) throws MetaDataAccessException 
        return (boolean) JdbcUtils.extractDatabaseMetaData(flywayConfiguration.getDataSource(),
                callback -> callback
                        .getColumns(null, null, flywayConfiguration.getTable(), "version_rank")
                        .next());
    

注意这里不需要手动调用 flyway.migrate()。

【讨论】:

BaseFlywayCallback 在 flyway 5.x.x 中被弃用。【参考方案4】:

上面的代码与版本 5 不兼容。它使用了已弃用的类。 这是一个更新的版本。

import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.callback.Context;
import org.flywaydb.core.api.callback.Event;
import org.flywaydb.core.api.configuration.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.stereotype.Component;

import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;

@Component
@Order(HIGHEST_PRECEDENCE)
@Slf4j
public class FlywayUpdate3To4Callback implements Callback 
    private final Flyway flyway;

    public FlywayUpdate3To4Callback(@Lazy Flyway flyway) 
        this.flyway = flyway;
    

    private boolean checkColumnExists(Configuration flywayConfiguration) throws MetaDataAccessException 
        return (boolean) JdbcUtils.extractDatabaseMetaData(flywayConfiguration.getDataSource(),
                callback -> callback
                        .getColumns(null, null, flywayConfiguration.getTable(), "version_rank")
                        .next());
    

    @Override
    public boolean supports(Event event, Context context) 
        return event == Event.BEFORE_VALIDATE;
    

    @Override
    public boolean canHandleInTransaction(Event event, Context context) 
        return false;
    

    @Override
    public void handle(Event event, Context context) 
        boolean versionRankColumnExists = false;
        try 
            versionRankColumnExists = checkColumnExists(context.getConfiguration());
         catch (MetaDataAccessException e) 
            log.error("Cannot obtain flyway metadata");
            return;
        
        if (versionRankColumnExists) 
            log.info("Upgrading metadata table the Flyway 4.0 format ...");
            Resource resource = new ClassPathResource("db/migration/flyway_upgradeMetaDataTable_V3_to_V4.sql",
                    Thread.currentThread().getContextClassLoader());
            ScriptUtils.executeSqlScript(context.getConnection(), resource);
            log.info("Flyway metadata table updated successfully.");
            // recalculate checksums
            flyway.repair();
        
    

【讨论】:

我使用了 Event.BEFORE_VALIDATE,因为在迁移之前调用了验证,因此当我使用 BEFORE_MIGRATE 时它对我来说失败了。 我可以确认此解决方案可以完美运行(使用 BEFORE_VALIDATE)将 Flyway 3.x 升级到 6.4.4。除了上面的代码,还需要准备这里提到的SQL脚本。从flyway-4.2.0 分支(查找upgradeMetaDataTable.sql)中找到与您的数据库技术相匹配的脚本,将里面的变量(架构和表名)替换为您的,然后将其放入db/migration/flyway_upgradeMetaDataTable_V3_to_V4.sql【参考方案5】:

我也尝试跳过 v4,但没有成功。从 3 到 5 运行修复将使校验和正确,但不会更改 schema_version 格式。这也发生了变化。

看来您需要先转到 v4。即使暂时只是为了运行mvn flyway:validate,也会修复schema_version

我在这个 repo 上做了这个:https://github.com/fabiofalci/flyway-from-3-to-5/commits/5.0.7

第一次提交是 v3,第二次提交是 v4(我在其中运行了验证),然后在 v5 上进行第三次提交,架构是正确的。

【讨论】:

感谢法比奥!不幸的是,要使其正常工作,需要先部署从第二次提交构建的应用程序,然后才能部署从第三次提交构建的应用程序。【参考方案6】:

它对我有用,除了我必须再次输入 Event.BEFORE_VALIDATE 而不是 FlywayUpdate3To4CallbackEvent.BEFORE_MIGRATE /strong> 类。这是因为我的校验和在已运行的迁移中无效,因此需要在验证之前而不是在迁移之前修复它。谢谢。

【讨论】:

从版本 3 更新到 4 时,Flyway 更新不会迁移 schema_version 表

】从版本3更新到4时,Flyway更新不会迁移schema_version表【英文标题】:Flywayupdatedoesnotmigrateschema_versiontablewhenupdatingfromversion3to4【发布时间】:2018-09-1916:48:50【问题描述】:我从SpringBoot1.5更新到2。和其他人一样,我也面临这样的问... 查看详情

根据 Flyway,原始 SQL 脚本现在无效

】根据Flyway,原始SQL脚本现在无效【英文标题】:OriginalSQLscriptnowinvalidaccordingtoFlyway【发布时间】:2021-11-0307:18:47【问题描述】:我们有一个已经投入生产一段时间的SpringBoot应用程序。我们使用Flyway来管理数据库迁移。我刚刚从... 查看详情

使用 DB2 驱动程序从 Flyway 命令行获取连接错误

】使用DB2驱动程序从Flyway命令行获取连接错误【英文标题】:GettingconnectionerrorfromFlywaycommandlinewithDB2drivers【发布时间】:2021-11-1609:38:20【问题描述】:我们正在将数据库版本从DB211.1.3升级到DB211.5.6。我们使用Flyway迁移代码已有4... 查看详情

Flyway 升级与过渡到在线模式迁移等

】Flyway升级与过渡到在线模式迁移等【英文标题】:Flywayupgradevs.transitiontoonlineschemamigration,etc【发布时间】:2021-01-0314:35:14【问题描述】:我们的主要项目从一开始就一直使用现在非常古老的Flyway版本。(v3.2.1)Flyway多年来进行了... 查看详情

Flyway 升级 4.2.0 -> 5.0.0 迁移失败,即使指定了 flyway.table

】Flyway升级4.2.0->5.0.0迁移失败,即使指定了flyway.table【英文标题】:Flywayupgrading4.2.0->5.0.0migratefailsevenwithflyway.tablespecified【发布时间】:2019-09-1303:59:38【问题描述】:我正在更新一些在我们的应用程序中落后的maven依赖项,... 查看详情

使用 Flyway 部署到多个模式

...不同的代码。我想在代码部署之前使用Flyway创建架构,但如何在单个配置文件中进行 查看详情

Flyway H2和MySql升级后不匹配

】FlywayH2和MySql升级后不匹配【英文标题】:FlywayH2andMySqlmismatchafterupgrading【发布时间】:2020-09-1108:23:37【问题描述】:我陷入了深深的泥潭:(我想将gradle从4升级到6。这导致我升级了spring,并最终升级了flyway和H2。现在,不幸的是... 查看详情

Heroku Postgres 升级后 Flyway 无法连接到数据库

】HerokuPostgres升级后Flyway无法连接到数据库【英文标题】:FlywaycannotconnecttodbafterHerokuPostgresupgrade【发布时间】:2017-02-2423:11:45【问题描述】:我正在将我的heroku数据库从业余开发者升级到标准0(使用官方说明https://devcenter.heroku.c... 查看详情

Flyway 5.0.7 关于使用 schema_version 表的警告

】Flyway5.0.7关于使用schema_version表的警告【英文标题】:Flyway5.0.7warningaboutusingschema_versiontable【发布时间】:2018-08-1007:54:31【问题描述】:我们使用FlywayGradle插件进行离线迁移(即我们在系统关闭时迁移)。我们最近升级到Flyway5.0... 查看详情

如何在 schema_version 升级之前执行 preFlywayUpgrade 脚本

】如何在schema_version升级之前执行preFlywayUpgrade脚本【英文标题】:HowtoexecuteapreFlywayUpgradescriptbeforeschema_versionupgrade【发布时间】:2016-12-1716:29:17【问题描述】:我们正在尝试从flyway2.0.3迁移到4.0.3。这失败了,因为flyway尝试从schema... 查看详情

如何将 JDBC 驱动程序添加到 Flyway Gradle 插件

】如何将JDBC驱动程序添加到FlywayGradle插件【英文标题】:HowtoaddJDBCdrivertoFlywayGradleplugin【发布时间】:2017-02-0523:48:35【问题描述】:我正在使用Gradle3.0,我想将Flyway任务添加到我的项目构建文件中。我在build.gradle中添加了以下内... 查看详情

重试flyway失败的迁移

】重试flyway失败的迁移【英文标题】:Retryaflywayfailedmigration【发布时间】:2012-08-0901:31:04【问题描述】:我只是在配置和完全理解flyway的过程中,我遇到了这种情况:我成功配置了一个新项目以使用flyway。我成功地将测试数据库... 查看详情

最佳实践:使用后如何修改flyway迁移脚本

】最佳实践:使用后如何修改flyway迁移脚本【英文标题】:Bestpractice:Howtomodifyflywaymigrationscriptafterithasbeenused【发布时间】:2016-05-3101:37:40【问题描述】:我正在寻找以下案例的建议。我在生产环境中设置了flyway迁移脚本。在每次... 查看详情

如何在 flyway 中压缩/合并迁移

】如何在flyway中压缩/合并迁移【英文标题】:Howtosquash/mergemigrationsinflyway【发布时间】:2014-10-1919:13:07【问题描述】:假设我有从V1_1到V1_300的迁移脚本-这是一个相当大的数字,需要很长时间。但是有时会有一个版本-我可以从飞... 查看详情

如何使用 Flyway 从 DB Schema 导出或导入数据

】如何使用Flyway从DBSchema导出或导入数据【英文标题】:HowtoexportorimportdatafromDBSchemausingFlyway【发布时间】:2019-07-1921:08:28【问题描述】:谁能解释一下如何配置flyway通过Jenkins导出或导入数据库架构。目前我在jenkins中配置了FlyWay... 查看详情

如何从 Flyway 迁移名称中删除前缀?

】如何从Flyway迁移名称中删除前缀?【英文标题】:HowtoremoveprefixfromFlywaymigrationname?【发布时间】:2021-11-2303:04:46【问题描述】:默认情况下,Flyway在文件夹db/migration中搜索以“V”开头的迁移,例如:V1_1_0__init_schema.sql但是,我... 查看详情

Flyway 可以从 jar 迁移吗?

...目。项目中有src/main/resources/db/migration/V1_1__create_table.sql。如何从这个jar运行迁移?我试过这个./flywaymigrate-url=jdbc:postgresql://127.0.0 查看详情

从特定版本开始 Flyway 迁移

】从特定版本开始Flyway迁移【英文标题】:StartFlywaymigrationfromspecificversion【发布时间】:2019-05-0418:35:20【问题描述】:我尝试使用flyway进行迁移。我找到了这个选项spring.flyway.target=#迁移应该达到的目标版本可以考虑。但我需要... 查看详情