处理许多生产环境中许多客户部署的产品。它包括至少一个Spring Boot应用程序。
我们使用flyway进行数据库架构迁移。从Spring Boot 1.5.x升级到2.0.x已使我们的flyway版本从3.x升级到5.x.
Spring Boot迁移指南只是说在升级之前升级到第4航道。但是,这需要我们的所有客户在升级到最新版本之前进行中间升级。
所以,问题是:你如何从flyway 3直接升级到5号航线?
答案 0 :(得分:5)
如果我不是这个星球上最后一个仍然从3升级到5的人。
<强>问题:强>
我希望升级对项目中的其他开发人员透明,并且在实时应用程序上升级时不需要任何特殊的部署指示,因此我做了以下操作。
我看看版本4如何处理升级:
这很容易手动完成,但要使其透明。该应用程序是一个弹簧应用程序,但不是一个春季启动应用程序,因此当我在应用程序启动时自动运行迁移,使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();
});
}
}
需要注意的一些事项:
答案 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
请注意,这将执行以下操作:
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。答案 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>类。这是因为我的校验和对已经运行的迁移无效,因此需要在验证之前进行固定,而不是在迁移之前进行固定。谢谢。