SpringBoot实现Flyway的Callback回调钩子
背景
产品迭代使用CI/CD升级过程中,需要对不同发布环境的不同产品版本进行数据库迭代升级,我们在中间某次产品迭代时加入了Flyway中间件以实现数据库结构的自动化升级。
需求
由于是迭代过程中加入的Flyway,而不是一开始就使用,所以Flyway的版本表和版本记录数据在已经发布过的环境中是不存在的,而且每个环境的产品版本也不同,数据库结构迭代升级首先需要确定当前产品的版本,再执行相应的升级脚本。所以我们需要在发布环境升级时,在Flyway执行之前先根据数据库现状写入版本表和版本记录数据,才能让Flyway正常执行迭代升级脚本。
Flyway Hooks/Callback
查阅官方文档知道Flyway有个Hooks,官网文档,文档详细描述如下:
Building upon that are the Java-based Callbacks when you need more power or flexibility in a Callback than SQL can offer you.
They can be created by implementing the Callback interface:
public class MyNotifierCallback implements Callback { // Ensures that this callback handles both events @Override public boolean supports(Event event, Context context) { return event.equals(Event.AFTER_MIGRATE) || event.equals(Event.AFTER_MIGRATE_ERROR); } // Not relevant if we don't interact with the database @Override public boolean canHandleInTransaction(Event event, Context context) { return true; } // Send a notification when either event happens. @Override public void handle(Event event, Context context) { String notification = event.equals(Event.AFTER_MIGRATE) ? "Success" : "Failed"; // ... Notification logic ... notificationService.send(notification); } String getCallbackName() { return "MyNotifier"; } }
In order to be picked up by Flyway, Java-based Callbacks must implement the Callback interface. Flyway will automatically scan for and load all callbacks found in the db/callback package. Additional callback classes or scan locations can be specified by the flyway.callbacks configuration property.
SpringBoot实现
根据官方文档描述,需要实现Callback并配置flyway.callbacks参数,但是在springboot配置文件中并没有找到关于spring.flyway.callbacks或者flyway.callbacks的配置项
查看了下源码找到了原因,callbacks属性被定义为了final,所以配置文件中不能设置callbacks配置项,关键代码截图如下:
public class Flyway implements FlywayConfiguration { private final List<FlywayCallback> callbacks; public void setCallbacks(FlywayCallback... callbacks) { this.callbacks.clear(); this.callbacks.addAll(Arrays.asList(callbacks)); } }
有个callbacks的set方法,可以尝试用spring注入的方式配置,实现代码如下:
import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.flywaydb.core.api.MigrationInfo; import org.flywaydb.core.api.callback.FlywayCallback; import org.springframework.context.annotation.Configuration; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * Flyway迭代升级SQL脚本钩子 * 主要作用: * 1、初始化VERSION表 * 2、写入当前迭代版本号,根据数据库中是否存在数据表判断 */ @Slf4j @Configuration public class InitFlywayCallback implements FlywayCallback { @Override public void beforeClean(Connection connection) { } @Override public void afterClean(Connection connection) { } @Override public void beforeMigrate(Connection connection) { } @Override public void afterMigrate(Connection connection) { } @Override public void beforeUndo(Connection connection) { } @Override public void beforeEachUndo(Connection connection, MigrationInfo migrationInfo) { } @Override public void afterEachUndo(Connection connection, MigrationInfo migrationInfo) { } @Override public void afterUndo(Connection connection) { } @Override public void beforeEachMigrate(Connection connection, MigrationInfo migrationInfo) { } @Override public void afterEachMigrate(Connection connection, MigrationInfo migrationInfo) { } @SneakyThrows @Override public void beforeValidate(Connection connection) { log.info("Flyway执行拦截"); } @Override public void afterValidate(Connection connection) { } @Override public void beforeBaseline(Connection connection) { } @Override public void afterBaseline(Connection connection) { } @Override public void beforeRepair(Connection connection) { } @Override public void afterRepair(Connection connection) { } @Override public void beforeInfo(Connection connection) { } @Override public void afterInfo(Connection connection) { } }
项目启动打印结果如下:
2020-12-23 09:42:53.025 dassets 13092 [--] [ INFO] [org.flywaydb.core.internal.util.VersionPrinter.info:44] [ main] [Flyway Community Edition 5.0.7 by Boxfuse] 2020-12-23 09:43:03.461 dassets 13092 [--] [ INFO] [org.flywaydb.core.internal.database.DatabaseFactory.info:44] [ main] [Database: jdbc:mysql://10.101.6.105:3306/user (MySQL 5.7)] 2020-12-23 09:43:03.675 dassets 13092 [--] [ INFO] [com.cestc.dassets.interceptor.InitFlywayCallback.beforeValidate:76] [ main] [Flyway执行拦截]
至此,callback执行成功!
最后附一个Flyway的Callback事件描述,官网文档:
| Name | Execution |
|---|---|
| beforeMigrate | Before Migrate runs |
| beforeRepeatables | Before all repeatable migrations during Migrate |
| beforeEachMigrate | Before every single migration during Migrate |
| beforeEachMigrateStatement Flyway Teams | Before every single statement of a migration during Migrate |
| afterEachMigrateStatement Flyway Teams | After every single successful statement of a migration during Migrate |
| afterEachMigrateStatementError Flyway Teams | After every single failed statement of a migration during Migrate |
| afterEachMigrate | After every single successful migration during Migrate |
| afterEachMigrateError | After every single failed migration during Migrate |
| afterMigrate | After successful Migrate runs |
| afterVersioned | After all versioned migrations during Migrate |
| afterMigrateError | After failed Migrate runs |
| beforeUndo Flyway Teams | Before Undo runs |
| beforeEachUndo Flyway Teams | Before every single migration during Undo |
| beforeEachUndoStatement Flyway Teams | Before every single statement of a migration during Undo |
| afterEachUndoStatement Flyway Teams | After every single successful statement of a migration during Undo |
| afterEachUndoStatementError Flyway Teams | After every single failed statement of a migration during Undo |
| afterEachUndo Flyway Teams | After every single successful migration during Undo |
| afterEachUndoError Flyway Teams | After every single failed migration during Undo |
| afterUndo Flyway Teams | After successful Undo runs |
| afterUndoError Flyway Teams | After failed Undo runs |
| beforeClean | Before Clean runs |
| afterClean | After successful Clean runs |
| afterCleanError | After failed Clean runs |
| beforeInfo | Before Info runs |
| afterInfo | After successful Info runs |
| afterInfoError | After failed Info runs |
| beforeValidate | Before Validate runs |
| afterValidate | After successful Validate runs |
| afterValidateError | After failed Validate runs |
| beforeBaseline | Before Baseline runs |
| afterBaseline | After successful Baseline runs |
| afterBaselineError | After failed Baseline runs |
| beforeRepair | Before Repair runs |
| afterRepair | After successful Repair runs |
| afterRepairError | After failed Repair runs |

浙公网安备 33010602011771号