Flyway - 在 "schema_version" / "flyway_schema_history" table 名称之间进行规范化 - "Found non-empty schema(s) "PUBLIC" 但没有架构历史记录 table"

Flyway - normalising between "schema_version" / "flyway_schema_history" table names - "Found non-empty schema(s) "PUBLIC" but no schema history table"

我一直在我的应用程序中使用 Flyway,其中每次使用都有自己的持久 H2 数据库。

由于数据库持续存在,并且 Flyway 从 v4 升级到 v5,一些用户有 schema_version table,而其他用户有 flyway_schema_history table。

显然 Flyway v6 不适用于具有 schema_version table.

的数据库

最初我被屏蔽了,但后来发现callbacks允许SQL在任意点被运行。所以显而易见的解决方案是 beforeMigrate.sqlschema_version table 重命名为 flyway_schema_history.

然而,当我尝试这样做时,即使我可以从调试日志中看到命令已执行,我也会收到以下错误。 当我在回调功能之外手动重命名数据库中的 table 时,我什至会收到错误消息。

org.flywaydb.core.api.FlywayException: Found non-empty schema(s) "PUBLIC" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.

无论我是暂时升级到 Flyway v6 还是最新的 v8,我都会遇到这个问题。

有什么明显的我遗漏的东西吗?或者是否有更智能的方法将这些现有数据库移动到与最新 flyway 版本兼容的格式?

我已经使用 Maven 创建了一个 MVCE,但它显然有点复杂,因为它涉及数据库等。

目录结构:

flywaytest
    src
        main
            java
                com
                    me
                        flywaytest
                            FlywayTest.java
            resources
                db
                    migration
                        beforeMigrate.sql
                        V1__Intial.sql
    pom.xml

FlywayTest.java:

package com.me.flywaytest;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.text.MessageFormat;

import org.flywaydb.core.Flyway;
import org.h2.engine.Constants;

public class FlywayTest {
    private static final String databaseName = "fruits";
    private static final String h2URL = MessageFormat.format("jdbc:h2:~/{0}", databaseName);
    private static final Path databaseLocation = Paths.get(System.getProperty("user.home"))
        .resolve(databaseName + Constants.SUFFIX_MV_FILE);

    public static void main(final String[] args) throws Exception {
        Files.deleteIfExists(databaseLocation);

        createOldDatabase();

        Flyway.configure().dataSource(h2URL, null, null).load().migrate();
    }

    private static Connection getConnection() throws Exception {
        return DriverManager.getConnection(h2URL, null, null);
    }

    /** Creates an example database as created by Flyway v4.2.0 */
    private static void createOldDatabase() throws Exception {
        // Old Flyway database generated by rolling back Flyway and running an initial
        // migration and dumping it's SQL using the following:
        // Flyway flyway = new Flyway();
        // flyway.setDataSource(h2URL, null, null);
        // flyway.migrate();
        // try (Connection connection = getConnection()) {
        // Script.process(connection,
        // Paths.get(System.getProperty("user.home")).resolve(databaseName +
        // ".sql").toString(), "", "");
        // }

        // Which created the following:
        final String fruitsSQL = """
            ;
        CREATE USER IF NOT EXISTS "" SALT '' HASH '' ADMIN;
        CREATE CACHED TABLE "PUBLIC"."schema_version"(
            "installed_rank" INT NOT NULL,
            "version" VARCHAR(50),
            "description" VARCHAR(200) NOT NULL,
            "type" VARCHAR(20) NOT NULL,
            "script" VARCHAR(1000) NOT NULL,
            "checksum" INT,
            "installed_by" VARCHAR(100) NOT NULL,
            "installed_on" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
            "execution_time" INT NOT NULL,
            "success" BOOLEAN NOT NULL
        );
        ALTER TABLE "PUBLIC"."schema_version" ADD CONSTRAINT "PUBLIC"."schema_version_pk" PRIMARY KEY("installed_rank");
        -- 1 +/- SELECT COUNT(*) FROM PUBLIC.schema_version;
        INSERT INTO "PUBLIC"."schema_version" VALUES
        (1, '1', 'Initial', 'SQL', 'V1__Initial.sql', 691111646, '', TIMESTAMP '2021-12-26 00:07:37.878797', 0, TRUE);
        CREATE INDEX "PUBLIC"."schema_version_s_idx" ON "PUBLIC"."schema_version"("success");
        CREATE CACHED TABLE "PUBLIC"."FRUITS"(
            "NAME" CHARACTER VARYING
        );
        -- 0 +/- SELECT COUNT(*) FROM PUBLIC.FRUITS;

        """;

        try (Statement statement = getConnection().createStatement()) {
            statement.execute(fruitsSQL);
        }
    }
}

beforeMigrate.sql:

ALTER TABLE IF EXISTS "schema_version" RENAME CONSTRAINT "schema_version_pk" TO "flyway_schema_history_pk";
ALTER TABLE IF EXISTS "schema_version" RENAME TO "flyway_schema_history";
ALTER INDEX IF EXISTS "schema_version_s_idx" RENAME TO "flyway_schema_history_s_idx";

V1__Initial.sql:

CREATE TABLE FRUITS(
    NAME CHARACTER VARYING
);

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.me</groupId>
    <artifactId>flywaytest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
            <version>8.3.0</version>
        </dependency>
    </dependencies>
</project>

我个人从未使用过 Flyway 的回调,但请注意,根据 Flyway class 中的 library source code, the beforeMigrate callback will be run in DbMigrate after the schema history table existence is checked,请尝试重命名历史记录 table可能不工作。

作为一种可能的解决方案,您可以 try providing explicitly the table name 为您的数据库保留前一个 schema_version。例如:

Flyway.configure()
  .table("schema_version")
  .dataSource(h2URL, null, null)
  .load()
  .migrate()
;

可能使用 beforeValidate 回调也可以,就像 that callback seems to be executed before the actual schema history table existence check 一样。我将您的脚本从 beforeMigrate.sql 重命名为 beforeValidate.sql,它运行正常。只需确保启用迁移验证 - 默认情况下应该启用。