背景
產品迭代使用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 |