如何从3号航站楼直接升级到5号航道

时间:2018-04-20 15:29:29

标签: flyway

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

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

Spring Boot迁移指南只是说在升级之前升级到第4航道。但是,这需要我们的所有客户在升级到最新版本之前进行中间升级。

所以,问题是:如何从flyway 3直接升级到5号航线?

6 个答案:

答案 0 :(得分:5)

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

<强>问题:

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

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

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

这很容易手动完成,但要使其透明。该应用程序是一个弹簧应用程序,但不是一个春季启动应用程序,因此当我在应用程序启动时自动运行迁移,使LocalContainerEntityManager bean构造依赖于flyway bean,这将调用migrate作为其init方法(在此解释{ {3}}),所以bootstrapping的顺序是:

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()稍微多一点,但我们很高兴接受数据库在运行之前必须处于良好状态

答案 1 :(得分:2)

如果您使用的是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()。

答案 2 :(得分:1)

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

好像你需要先去v4。即使只是暂时运行mvn flyway:validate,也会修复schema_version

我已在此回购邮件中完成此操作:https://github.com/fabiofalci/flyway-from-3-to-5/commits/5.0.7

第一次提交是v3,第二次提交是v4(我运行validate),然后第五次提交v5,架构是正确的。

答案 3 :(得分:1)

第0步。

升级到Spring Boot v2.1(以及therby隐式升级到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的一个,并假设飞行路线表的名称为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步。

运行spring boot。

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

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

积分

此答案基于Eduardo Rodrigues的答案,方法是更改​​:

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

答案 4 :(得分:0)

上面的代码与版本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_MIGRATE;
    }

    @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("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();
        }
    }
}

答案 5 :(得分:0)

它对我有用,除了我不得不再次放置 Event.BEFORE_VALIDATE 而不是上一版 FlywayUpdate3To4Callback < / strong>类。这是因为我的校验和对已经运行的迁移无效,因此需要在验证之前进行固定,而不是在迁移之前进行固定。谢谢。