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事件描述,官网文档

NameExecution
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
posted @ 2020-12-23 09:50  codest  阅读(1380)  评论(0编辑  收藏  举报