Spring Boot + Hibernate + Flyway:不要 运行 在新数据库上迁移
Spring Boot + Hibernate + Flyway: don't run migrations on new database
我正在使用 Flyway 更新数据库架构。当前,最新版本的架构是 3(最新的迁移文件名为 V3__postgres.sql
)。
如果我 运行 具有较旧模式版本的数据库上的应用程序,Flyway 会按预期执行更新脚本。但是,如果我 运行 一个新的(空)数据库上的应用程序,flyway 会尝试执行更新脚本,但它找不到任何 tables(因为 Hibernate 还没有创建它们) ,应用程序因错误而终止。
我希望 Flyway 不 在空数据库上执行更新脚本,因为当 Hibernate 创建 tables 时,它们无论如何都会是最新版本.
如果我理解正确的话,我应该可以使用参数flyway.baseline-version
来完成这个。我的理论是,如果 Flyway 没有找到 table schema_version
,它应该创建它并插入一条记录,说明数据库是版本 3。但即使我设置 flyway.baseline-version=3
,Flyway无论如何执行脚本。我还尝试设置参数 flyway.baseline-on-migrate=true
及其不同的组合,但无法正常工作。
我对 baseline-version
参数的理解是否正确,还是我遗漏了什么?
注意: 我知道自从 Spring Boot 2 以来,参数命名空间已更改为 spring.flyway.*
,但我正在使用 Spring Boot 1 所以这不是问题。
正如评论已经提到的,flyway 和 hibernate 不应该一起使用来更新模式,但这并不意味着你不能使用 Hibernate 来帮助你维护你的模式。
基线:
此功能绝对不是为了防止在空数据库上执行迁移而设计的。这应该在您的数据库已经存在时使用(即它已经有表和数据,并且您想保留这些数据)。对于空数据库,它是无用的。
示例:
假设您有一个现有的数据库,由 2 个脚本生成:
V1__create_tables.sql
V2__create_constraints.sql
现在您想使用 flyway 管理进一步的模式更新:
V3__First_update.sql
V4__Second_update.sql
V2 是您的基线,意味着数据库迁移不会执行迁移 V1 和 V2,因为它们已经存在。
如果你想组合Spring Boot + Hibernate + Flyway:
- 禁用休眠模式自动更新 - 如前所述,这可能很危险 (hbm2ddl.auto=false)
- 使用 Hibernate SchemaGenerator 生成一个大的 SQL 文件,我们称之为
V1__initial_schema.sql
,就像这样 old-but-still-valid article.
- 如果你正好有一个V1的数据库data you want to keep, you should baseline it。否则就从一个空模式开始。 (
flyway clean
) 和迁移 (flyway migrate
)。 警告:flyway clean
将删除您所有的表格!
现在您已准备好对每个即将进行的架构修改使用 Hibernate 和 Flyway。
假设您刚刚更新了模型:
- 使用 Hibernate SchemaGenerator 生成相同的大 SQL 文件,我们称之为
generator-output.sql
.
- 对
V1__initial_schema.sql
和 generator-output.sql
进行并排比较。这将帮助您识别这两个文件之间的差异。根据这些差异,您可以生成一个新的迁移文件,我们称之为V2__update.sql
.
- 对您的数据库执行
flyway migrate
。
已解决:我创建了一个自定义 FlywayMigrationStrategy
bean,我在其中手动检查 Flyway 是否已引入数据库(通过检查迁移 table 是否存在)。如果没有,我运行 baseline
命令。然后我像往常一样调用 migrate
命令。
这里是 Spring 引导配置:
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FlywayConfig {
@Autowired
private DataSource dataSource;
@Value("${flyway.table}")
private String flywayTableName;
@Value("${flyway.baselineVersionAsString}")
private String baselineVersion;
@Bean
public FlywayMigrationStrategy flywayMigrationStrategy() {
return flyway -> {
if (!isFlywayInitialized()) {
flyway.setBaselineVersionAsString(baselineVersion);
flyway.baseline();
}
flyway.migrate();
};
}
private boolean isFlywayInitialized() {
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metadata = connection.getMetaData();
ResultSet result = metadata.getTables(null, null, flywayTableName, null);
return result.next();
} catch (SQLException e) {
throw new RuntimeException("Failed to check if Flyway is initialized", e);
}
}
}
顺便说一下,当我尝试插入新版本时,我发现区分大小写的文件名很重要。
另外每次记得设置这个 属性: spring.flyway.ignore-future-migrations=false
我用 V100__script.sql
代替 v100__script.sql
我正在使用 Flyway 更新数据库架构。当前,最新版本的架构是 3(最新的迁移文件名为 V3__postgres.sql
)。
如果我 运行 具有较旧模式版本的数据库上的应用程序,Flyway 会按预期执行更新脚本。但是,如果我 运行 一个新的(空)数据库上的应用程序,flyway 会尝试执行更新脚本,但它找不到任何 tables(因为 Hibernate 还没有创建它们) ,应用程序因错误而终止。
我希望 Flyway 不 在空数据库上执行更新脚本,因为当 Hibernate 创建 tables 时,它们无论如何都会是最新版本.
如果我理解正确的话,我应该可以使用参数flyway.baseline-version
来完成这个。我的理论是,如果 Flyway 没有找到 table schema_version
,它应该创建它并插入一条记录,说明数据库是版本 3。但即使我设置 flyway.baseline-version=3
,Flyway无论如何执行脚本。我还尝试设置参数 flyway.baseline-on-migrate=true
及其不同的组合,但无法正常工作。
我对 baseline-version
参数的理解是否正确,还是我遗漏了什么?
注意: 我知道自从 Spring Boot 2 以来,参数命名空间已更改为 spring.flyway.*
,但我正在使用 Spring Boot 1 所以这不是问题。
正如评论已经提到的,flyway 和 hibernate 不应该一起使用来更新模式,但这并不意味着你不能使用 Hibernate 来帮助你维护你的模式。
基线: 此功能绝对不是为了防止在空数据库上执行迁移而设计的。这应该在您的数据库已经存在时使用(即它已经有表和数据,并且您想保留这些数据)。对于空数据库,它是无用的。
示例: 假设您有一个现有的数据库,由 2 个脚本生成:
V1__create_tables.sql
V2__create_constraints.sql
现在您想使用 flyway 管理进一步的模式更新:
V3__First_update.sql
V4__Second_update.sql
V2 是您的基线,意味着数据库迁移不会执行迁移 V1 和 V2,因为它们已经存在。
如果你想组合Spring Boot + Hibernate + Flyway:
- 禁用休眠模式自动更新 - 如前所述,这可能很危险 (hbm2ddl.auto=false)
- 使用 Hibernate SchemaGenerator 生成一个大的 SQL 文件,我们称之为
V1__initial_schema.sql
,就像这样 old-but-still-valid article. - 如果你正好有一个V1的数据库data you want to keep, you should baseline it。否则就从一个空模式开始。 (
flyway clean
) 和迁移 (flyway migrate
)。 警告:flyway clean
将删除您所有的表格!
现在您已准备好对每个即将进行的架构修改使用 Hibernate 和 Flyway。 假设您刚刚更新了模型:
- 使用 Hibernate SchemaGenerator 生成相同的大 SQL 文件,我们称之为
generator-output.sql
. - 对
V1__initial_schema.sql
和generator-output.sql
进行并排比较。这将帮助您识别这两个文件之间的差异。根据这些差异,您可以生成一个新的迁移文件,我们称之为V2__update.sql
. - 对您的数据库执行
flyway migrate
。
已解决:我创建了一个自定义 FlywayMigrationStrategy
bean,我在其中手动检查 Flyway 是否已引入数据库(通过检查迁移 table 是否存在)。如果没有,我运行 baseline
命令。然后我像往常一样调用 migrate
命令。
这里是 Spring 引导配置:
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FlywayConfig {
@Autowired
private DataSource dataSource;
@Value("${flyway.table}")
private String flywayTableName;
@Value("${flyway.baselineVersionAsString}")
private String baselineVersion;
@Bean
public FlywayMigrationStrategy flywayMigrationStrategy() {
return flyway -> {
if (!isFlywayInitialized()) {
flyway.setBaselineVersionAsString(baselineVersion);
flyway.baseline();
}
flyway.migrate();
};
}
private boolean isFlywayInitialized() {
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metadata = connection.getMetaData();
ResultSet result = metadata.getTables(null, null, flywayTableName, null);
return result.next();
} catch (SQLException e) {
throw new RuntimeException("Failed to check if Flyway is initialized", e);
}
}
}
顺便说一下,当我尝试插入新版本时,我发现区分大小写的文件名很重要。
另外每次记得设置这个 属性: spring.flyway.ignore-future-migrations=false
我用 V100__script.sql
代替 v100__script.sql