1. 概述

在这个教程中,我们将学习如何使用Flyway进行数据库迁移,并且会看到一个特定场景,即我们需要按非顺序运行迁移。

2. Flyway简介

Flyway是一个通过迁移脚本来帮助管理数据库版本化的工具。 我们可以创建改变数据库状态的脚本,我们称之为迁移。

在许多情况下,我们需要使用迁移。例如,可能需要从先前的数据源填充数据库。或者我们已经有一个依赖于特定数据库模式的应用程序,而我们需要部署新版本,该版本依赖于修改后的数据库结构。在这些情况下,我们可以使用迁移来实现所需的结果。

通过Flyway,我们甚至可以将这些脚本上传到版本控制系统,以便追踪何时以及为什么需要引入特定的修改。

在这个例子中,我们将使用**版本化迁移**。也就是说,我们将为每个迁移脚本分配一个版本号,以确定它们的执行顺序。

3. 示例迁移

在我们的示例中,我们将从一个简单的Spring Boot应用开始,其中包含Flyway作为起点。

3.1. Maven插件

首先,让我们在pom.xml中添加Flyway Maven插件

<build>
    ...
    <plugin>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-maven-plugin</artifactId>
        <version>10.7.1</version>
    </plugin>
    ...
</build>

我们需要这个插件来执行不同的Flyway目标。但在使用它之前,我们需要配置插件,包括设置数据库URL、用户名和密码等。

3.2. 迁移脚本

现在,在项目中的db/out-of-order-migration目录下创建两个SQL迁移脚本。 对于这些文件,我们需要遵循一个命名约定。让我们将第一个脚本命名为V1_0__create_city_table.sql

create table city (
  id numeric,
  name varchar(50),
  constraint pk_city primary key (id)
);

然后创建另一个名为V2_0__create_person_table.sql的脚本:

create table person (
  id numeric,
  name varchar(50),
  constraint pk_person primary key (id)
);

运行这两个脚本:

mvn -Dflyway.user=sa -Dflyway.url=jdbc:h2:file:./database -Dflyway.locations=filesystem:db/out-of-order-migration flyway:migrate

在这个命令中,我们使用了Flyway插件的*migrate*目标**,并提供了与数据库相关的三个参数。首先设置用户名,然后是数据库所在的URL,最后是迁移脚本的位置。

执行此命令后,Flyway成功运行了我们的两个脚本。我们还可以检查状态:

mvn -Dflyway.user=sa -Dflyway.url=jdbc:h2:file:./database -Dflyway.locations=filesystem:db/out-of-order-migration flyway:info

这将打印以下信息:

Schema version: 2.0
+-----------+---------+---------------------+------+---------------------+---------+
| Category  | Version | Description         | Type | Installed On        | State   |
+-----------+---------+---------------------+------+---------------------+---------+
| Versioned | 1.0     | create city table   | SQL  | 2023-01-02 21:08:45 | Success |
| Versioned | 2.0     | create person table | SQL  | 2023-01-02 21:08:45 | Success |
+-----------+---------+---------------------+------+---------------------+---------+

4. 非顺序迁移

当我们添加新的迁移时,Flyway可以检测到变化并执行最新的迁移。然而,当最新脚本的版本号不是最高的时候,问题就出现了。换句话说,它不在正确的顺序中。

默认情况下,Flyway会忽略我们的最新迁移。幸运的是,我们可以解决这个问题。我们可以使用*outOfOrder*配置参数告诉Flyway运行这些脚本,而不是跳过它们。

让我们在项目中尝试这个方法,添加一个新的迁移,名为V1_1__add_zipcode_to_city.sql

alter table city add column (
  zip varchar(10)
);

这个脚本的版本为1.1,但根据Flyway,我们已经迁移到了2.0版本。这意味着脚本会被忽略。我们可以通过info命令来确认这一点:

mvn -Dflyway.user=sa -Dflyway.url=jdbc:h2:file:./database -Dflyway.locations=filesystem:db/out-of-order-migration flyway:info

Flyway识别到了脚本,但状态被忽略了:

+-----------+---------+---------------------+------+---------------------+---------+
| Category  | Version | Description         | Type | Installed On        | State   |
+-----------+---------+---------------------+------+---------------------+---------+
| Versioned | 1.0     | create city table   | SQL  | 2023-01-02 21:08:45 | Success |
| Versioned | 2.0     | create person table | SQL  | 2023-01-02 21:08:45 | Success |
| Versioned | 1.1     | add zipcode to city | SQL  |                     | Ignored |
+-----------+---------+---------------------+------+---------------------+---------+

现在,如果我们再次获取状态,但加上outOfOrder标志,结果会有所不同:

mvn -Dflyway.user=sa -Dflyway.url=jdbc:h2:file:./database -Dflyway.locations=filesystem:db/out-of-order-migration -Dflyway.outOfOrder=true flyway:info

最新迁移的状态变为“待执行”:

+-----------+---------+---------------------+------+---------------------+---------+
| Category  | Version | Description         | Type | Installed On        | State   |
+-----------+---------+---------------------+------+---------------------+---------+
| Versioned | 1.0     | create city table   | SQL  | 2023-01-02 21:08:45 | Success |
| Versioned | 2.0     | create person table | SQL  | 2023-01-02 21:08:45 | Success |
| Versioned | 1.1     | add zipcode to city | SQL  |                     | Pending |
+-----------+---------+---------------------+------+---------------------+---------+

这意味着我们可以运行migrate命令并应用更改。同样,这里我们也必须加上outOfOrder标志:

mvn -Dflyway.user=sa -Dflyway.url=jdbc:h2:file:./database -Dflyway.locations=filesystem:db/out-of-order-migration -Dflyway.outOfOrder=true flyway:migrate

我们成功执行了新更改:

[INFO] Successfully validated 3 migrations (execution time 00:00.015s)
[INFO] Current version of schema "PUBLIC": 2.0
[WARNING] outOfOrder mode is active. Migration of schema "PUBLIC" may not be reproducible.
[INFO] Migrating schema "PUBLIC" to version "1.1 - add zipcode to city" [out of order]
[INFO] Successfully applied 1 migration to schema "PUBLIC", now at version v1.1 (execution time 00:00.019s)

这类迁移在被Flyway应用后会有不同的状态。 我们的前两个迁移处于“成功”状态,但第三个迁移虽然成功,但状态显示为“非顺序”:

+-----------+---------+---------------------+------+---------------------+--------------+
| Category  | Version | Description         | Type | Installed On        | State        |
+-----------+---------+---------------------+------+---------------------+--------------+
| Versioned | 1.0     | create city table   | SQL  | 2023-01-02 21:08:45 | Success      |
| Versioned | 2.0     | create person table | SQL  | 2023-01-02 21:08:45 | Success      |
| Versioned | 1.1     | add zipcode to city | SQL  | 2023-01-02 21:17:38 | Out of Order |
+-----------+---------+---------------------+------+---------------------+--------------+

5. 总结

在这篇教程中,我们简要介绍了Flyway迁移,并随后关注了一个特定场景。我们找到了一种方法来运行那些根据版本号被认为是顺序错误的迁移。 然后,我们在项目中应用了这一解决方案。

如往常一样,这些示例的源代码可以在GitHub上找到