1. 引言

Flyway库通过跟踪以SQL源代码形式存储的变更来实现数据库版本控制。*每个变更集被称为一个迁移(migration)***。

这些迁移会使用一组命令(包括migratecleaninfovalidatebaselinerepair)按顺序应用到数据库。应用过程会根据目标数据库的当前版本进行严格控制。

虽然常规迁移足以覆盖大多数场景,但回调机制在特定情况下能提供更灵活的解决方案。本文将探讨如何利用Flyway回调机制,在其提供的各种命令生命周期中插入自定义逻辑。

2. 典型应用场景

当遇到需要高度灵活性的特殊需求时,回调机制就能派上用场。以下是几个典型场景:

  • 重建物化视图 – 当迁移操作影响物化视图的基表时,我们可能需要重建物化视图。SQL回调非常适合执行这类逻辑
  • 刷新缓存 – 如果迁移修改了缓存中的数据,我们可以用回调清除缓存,确保应用从数据库获取最新数据
  • 调用外部系统 – 通过回调,我们可以调用外部系统(使用任意技术)。例如发布事件、发送邮件或触发服务器重启

3. 支持的回调类型

每个Flyway命令都有对应的beforeafter回调事件。关于这些命令的详细信息,可参考Flyway核心文章官方文档

  • BEFORE_ 事件在操作执行前触发
  • AFTER_ 事件在操作成功后触发,还包括两个更细粒度的事件:
    • ERROR 等价事件在操作失败后触发
    • OPERATION_FINISH 事件在操作完成后触发
  • *migrate和*undo命令还提供*_EACH*事件**,为每个单独的迁移触发。这两个命令需要额外回调是因为它们通常涉及多个迁移的执行

完整回调事件列表见Event类文档

例如,clean命令的回调事件是BEFORE_CLEANAFTER_CLEAN,分别在命令执行前后立即触发。回顾引言部分,可用命令包括:migratecleaninfovalidatebaselinerepair

Flyway提供这些额外钩子,让我们能在最细粒度(即单个迁移)上控制自定义回调逻辑。

4. 依赖配置

通过实际示例了解回调机制。首先在pom.xml中声明flyway-core依赖:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
    <version>9.16.3</version>
</dependency>

最新版本可在Maven中央仓库获取。

5. 回调实现方式

Flyway支持两种回调实现:Java和SQL。前者灵活性最高,可执行任意代码;后者则直接与数据库交互。

5.1. Java回调

Java API契约由Callback接口定义。最简单的自定义回调实现方式如下:

public class ExampleFlywayCallback implements Callback {

    private final Log log = LogFactory.getLog(getClass());

    @Override
    public boolean supports(Event event, Context context) {
        return event == Event.AFTER_EACH_MIGRATE;
    }

    @Override
    public boolean canHandleInTransaction(Event event, Context context) {
        return true;
    }

    @Override
    public void handle(Event event, Context context) {
        if (event == Event.AFTER_EACH_MIGRATE) {
            log.info("> afterEachMigrate");
        }
    }

    @Override
    public String getCallbackName() {
        return ExampleFlywayCallback.class.getSimpleName();
    }
}

5.2. SQL回调

SQL回调通过特定命名文件定义,这些文件需存放在配置为*locations(s)*的目录中。Flyway会在配置位置查找SQL回调文件并执行。

例如,在配置为location的目录中创建beforeEachMigrate.sql文件,该脚本会在migrate命令执行期间、每个迁移脚本运行前执行。

6. 配置与执行

以下示例同时配置了Java回调和两个SQL脚本位置(一个存放迁移脚本,一个存放SQL回调):

@Test
public void migrateWithSqlAndJavaCallbacks() {
    Flyway flyway = Flyway.configure()
      .dataSource(dataSource)
      .locations("db/migration", "db/callbacks")
      .callbacks(new ExampleFlywayCallback())
      .load();
    flyway.migrate();
}

⚠️ 注意:虽然迁移和SQL回调可以放在同一位置,但本示例分开存放以演示隔离管理。

当Java和SQL同时定义beforeEachMigrate回调时:

  1. Java回调优先执行
  2. 紧接着执行SQL回调

测试输出清晰展示了执行顺序:

21:50:45.677 [main] INFO  c.b.f.FlywayApplicationUnitTest - > migrateWithSqlAndJavaCallbacks
21:50:45.848 [main] INFO  o.f.c.i.license.VersionPrinter - Flyway Community Edition 8.0.0 by Redgate
21:50:45.849 [main] INFO  o.f.c.i.d.base.BaseDatabaseType - Database: jdbc:h2:mem:DATABASE (H2 1.4)
21:50:45.938 [main] INFO  o.f.core.internal.command.DbValidate - Successfully validated 2 migrations (execution time 00:00.021s)
21:50:45.951 [main] INFO  o.f.c.i.s.JdbcTableSchemaHistory - Creating Schema History table "PUBLIC"."flyway_schema_history" ...
21:50:46.003 [main] INFO  o.f.c.i.c.SqlScriptCallbackFactory - Executing SQL callback: beforeMigrate - 
21:50:46.015 [main] INFO  o.f.core.internal.command.DbMigrate - Current version of schema "PUBLIC": << Empty Schema >>
21:50:46.023 [main] INFO  o.f.c.i.c.SqlScriptCallbackFactory - Executing SQL callback: beforeEachMigrate - 
21:50:46.024 [main] INFO  o.f.core.internal.command.DbMigrate - Migrating schema "PUBLIC" to version "1.0 - add table one"
21:50:46.025 [main] INFO  c.b.f.ExampleFlywayCallback - > afterEachMigrate
21:50:46.046 [main] INFO  o.f.c.i.c.SqlScriptCallbackFactory - Executing SQL callback: beforeEachMigrate - 
21:50:46.046 [main] INFO  o.f.core.internal.command.DbMigrate - Migrating schema "PUBLIC" to version "1.1 - add table two"
21:50:46.047 [main] INFO  c.b.f.ExampleFlywayCallback - > afterEachMigrate
21:50:46.067 [main] INFO  o.f.core.internal.command.DbMigrate - Successfully applied 2 migrations to schema "PUBLIC", now at version v1.1 (execution time 00:00.060s)

7. 总结

本文详细介绍了Flyway回调机制在Java和SQL中的实现方式,分析了典型应用场景并通过示例演示了具体用法。掌握回调机制能帮我们解决迁移过程中的特殊需求,避免踩坑。

完整源代码可在GitHub仓库获取。


原始标题:A Guide to Flyway Callbacks