Using Flyway

Flyway 是一个流行的数据库迁移工具,通常在 JVM 环境中使用。

Flyway is a popular database migration tool that is commonly used in JVM environments.

Quarkus 提供了一流的支持来使用 Flyway,正如本指南中所解释的那样。

Quarkus provides first class support for using Flyway as will be explained in this guide.

Setting up support for Flyway

正如 [developing-with-flyway] 部分中所示,要开始在项目中使用 Flyway,您只需:

As shown in the [developing-with-flyway] section, to start using Flyway with your project, you just need to:

  • add your migrations to the src/main/resources/db/migration folder as you usually do with Flyway

  • activate the migrate-at-start option to migrate the schema automatically or inject the Flyway object and run your migration as you normally do

在构建文件中,添加以下依赖项:

In your build file, add the following dependencies:

  • the Flyway extension

  • your JDBC driver extension (quarkus-jdbc-postgresql, quarkus-jdbc-h2, quarkus-jdbc-mariadb, …​)

  • unless you’re using in-memory or file databases (such as H2 or SQLite), you need to add a flyway module dependency corresponding to the database you’re using. (for more details)

pom.xml
<!-- Flyway specific dependencies -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-flyway</artifactId>
</dependency>

<!-- JDBC driver dependencies -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

<!-- Flyway SQL Server specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-sqlserver</artifactId>
</dependency>

<!-- Flyway MariaDB/MySQL specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-mysql</artifactId>
</dependency>

<!-- Flyway Oracle specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-oracle</artifactId>
</dependency>

<!-- Flyway Postgres specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-postgresql</artifactId>
</dependency>

<!-- Flyway DB2 specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-db2</artifactId>
</dependency>

<!-- Derby specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-derby</artifactId>
</dependency>

<!-- HSQLDB specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-hsqldb</artifactId>
</dependency>

<!-- Informix specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-informix</artifactId>
</dependency>

<!-- Redshift specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-redshift</artifactId>
</dependency>

<!-- Saphana specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-saphana</artifactId>
</dependency>

<!-- Snowflake specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-snowflake</artifactId>
</dependency>

<!-- Sybasease specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-sybasease</artifactId>
</dependency>

<!-- Firebird specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-firebird</artifactId>
</dependency>

<!-- BigQuery specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-gcp-bigquery</artifactId>
</dependency>

<!-- Spanner specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-gcp-spanner</artifactId>
</dependency>

<!-- Singlestore specific dependencies -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-singlestore</artifactId>
</dependency>
build.gradle
// Flyway specific dependencies
implementation("io.quarkus:quarkus-flyway")
// JDBC driver dependencies
implementation("io.quarkus:quarkus-jdbc-postgresql")
// Flyway SQL Server specific dependencies
implementation("org.flywaydb:flyway-sqlserver")
// Flyway MariaDB/MySQL specific dependencies
implementation("org.flywaydb:flyway-mysql")
// Flyway Oracle specific dependencies
implementation("org.flywaydb:flyway-database-oracle")
// Flyway Postgres specific dependencies
implementation("org.flywaydb:flyway-database-postgresql")
// Flyway DB2 specific dependencies
implementation("org.flywaydb:flyway-database-db2")
// Flyway Derby specific dependencies
implementation("org.flywaydb:flyway-database-derby")
// HSQLDB specific dependencies
implementation("org.flywaydb:flyway-database-hsqldb")
// Informix specific dependencies
implementation("org.flywaydb:flyway-database-informix")
// Redshift specific dependencies
implementation("org.flywaydb:flyway-database-redshift")
// Saphana specific dependencies
implementation("org.flywaydb:flyway-database-saphana")
// Snowflake specific dependencies
implementation("org.flywaydb:flyway-database-snowflake")
// Sybasease specific dependencies
implementation("org.flywaydb:flyway-database-sybasease")
// Firebird specific dependencies
implementation("org.flywaydb:flyway-firebird")
// BigQuery specific dependencies
implementation("org.flywaydb:flyway-gcp-bigquery")
// Spanner specific dependencies
implementation("org.flywaydb:flyway-gcp-spanner")
// Singlestore specific dependencies
implementation("org.flywaydb:flyway-singlestore:10.15.0")

Flyway 支持依赖于 Quarkus 的数据源配置。它可以针对默认数据源以及每个 named datasource 进行自定义。首先,您需要将数据源配置添加到 application.properties 文件中,以允许 Flyway 管理架构。此外,您可以使用以下属性自定义 Flyway 行为:

Flyway support relies on the Quarkus datasource config. It can be customized for the default datasource as well as for every multiple-datasources. First, you need to add the datasource config to the application.properties file in order to allow Flyway to manage the schema. Also, you can customize the Flyway behaviour by using the following properties:

Unresolved directive in flyway.adoc - include::{generated-dir}/config/quarkus-flyway.adoc[]

Developing with Flyway

以下是 application.properties 文件的一个示例:

The following is an example for the application.properties file:

# configure your datasource
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=sarah
quarkus.datasource.password=connor
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase

# Run Flyway migrations automatically
quarkus.flyway.migrate-at-start=true

# More Flyway configuration options
# quarkus.flyway.baseline-on-migrate=true
# quarkus.flyway.baseline-version=1.0.0
# quarkus.flyway.baseline-description=Initial version
# quarkus.flyway.connect-retries=10
# quarkus.flyway.schemas=TEST_SCHEMA
# quarkus.flyway.table=flyway_quarkus_history
# quarkus.flyway.locations=db/location1,db/location2
# quarkus.flyway.sql-migration-prefix=X
# quarkus.flyway.repeatable-sql-migration-prefix=K

按照 Flyway 命名约定在默认文件夹中添加一个 SQL 迁移:src/main/resources/db/migration/V1.0.0__Quarkus.sql

Add a SQL migration to the default folder following the Flyway naming conventions: src/main/resources/db/migration/V1.0.0__Quarkus.sql

CREATE TABLE quarkus
(
  id   INT,
  name VARCHAR(20)
);
INSERT INTO quarkus(id, name)
VALUES (1, 'QUARKED');

现在,您可以启动您的应用程序,并且 Quarkus 将根据您的配置运行 Flyway 的 migrate 方法。

Now you can start your application and Quarkus will run the Flyway’s migrate method according to your config.

使用 quarkus.flyway.migrate-at-start=true,就像上面示例中一样,Quarkus 将在 application startup 的一部分中执行 Flyway 迁移。

With quarkus.flyway.migrate-at-start=true, as in the example above, Quarkus will execute the Flyway migration as part of the application startup.

@ApplicationScoped
public class MigrationService {
    // You can Inject the object if you want to use it manually
    @Inject
    Flyway flyway; 1

    public void checkMigration() {
        // This will print 1.0.0
        System.out.println(flyway.info().current().getVersion().toString());
    }
}
1 Inject the Flyway object if you want to use it directly

在开发模式下,如果任何现有的迁移脚本被修改,Quarkus 将自动重启应用程序。如果您希望在开发和测试新迁移脚本时利用这一优势,您将需要设置 %dev.quarkus.flyway.clean-at-start=true,以便 Flyway 实际运行修改后的迁移。

In dev mode Quarkus will automatically restart the application if any of the existing migration scripts get modified. If you want to take advantage of this while developing and testing new migration scripts, you will want to set %dev.quarkus.flyway.clean-at-start=true, so that Flyway actually runs the modified migration.

Repairing the Flyway schema history table

不同的场景可能需要 repairing Flyway 架构历史表。其中一个场景是在不支持事务性 DDL 语句的数据库中迁移失败时。

There are different scenarios which may require repairing the Flyway schema history table. One such scenario is when a migration fails in a database which doesn’t support transactional DDL statements.

在这种情况下, Flyway repair command 非常有用。在 Quarkus 中,可以通过设置 quarkus.flyway.repair-at-start=true 在迁移之前自动执行此操作,或者通过注入 Flyway 对象并调用 Flyway#repair() 手动执行此操作。

In such situations the Flyway repair command comes in handy. In Quarkus this can either be executed automatically before the migration by setting quarkus.flyway.repair-at-start=true or manually by injecting the Flyway object and calling Flyway#repair().

Multiple datasources

可以为多个数据源配置 Flyway。Flyway 属性的前缀与命名的数据源完全相同,例如:

Flyway can be configured for multiple datasources. The Flyway properties are prefixed exactly the same way as the named datasources, for example:

quarkus.datasource.db-kind=h2
quarkus.datasource.username=username-default
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default
quarkus.datasource.jdbc.max-size=13

quarkus.datasource.users.db-kind=h2
quarkus.datasource.users.username=username1
quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost/mem:users
quarkus.datasource.users.jdbc.max-size=11

quarkus.datasource.inventory.db-kind=h2
quarkus.datasource.inventory.username=username2
quarkus.datasource.inventory.jdbc.url=jdbc:h2:tcp://localhost/mem:inventory
quarkus.datasource.inventory.jdbc.max-size=12

# Flyway configuration for the default datasource
quarkus.flyway.schemas=DEFAULT_TEST_SCHEMA
quarkus.flyway.locations=db/default/location1,db/default/location2
quarkus.flyway.migrate-at-start=true

# Flyway configuration for the "users" datasource
quarkus.flyway.users.schemas=USERS_TEST_SCHEMA
quarkus.flyway.users.locations=db/users/location1,db/users/location2
quarkus.flyway.users.migrate-at-start=true

# Flyway configuration for the "inventory" datasource
quarkus.flyway.inventory.schemas=INVENTORY_TEST_SCHEMA
quarkus.flyway.inventory.locations=db/inventory/location1,db/inventory/location2
quarkus.flyway.inventory.migrate-at-start=true

注意,键中有一个额外位。语法如下:quarkus.flyway.[optional name.][datasource property]

Notice there’s an extra bit in the key. The syntax is as follows: quarkus.flyway.[optional name.][datasource property].

在未配置时,Flyway 会使用默认设置,针对每个数据源进行设置。

Without configuration, Flyway is set up for every datasource using the default settings.

Customizing Flyway

在除了 Quarkus 提供的配置选项之外还需要配置 Flyway 的情况下,io.quarkus.flyway.FlywayConfigurationCustomizer 类派得上用场。

In cases where Flyway needs to be configured in addition to the configuration options that Quarkus provides, the io.quarkus.flyway.FlywayConfigurationCustomizer class comes in handy.

若要针对默认数据源自定义 Flyway,只需添加一个 bean 如下所示:

To customize Flyway for the default datasource, simply add a bean like so:

@Singleton
public static class MyCustomizer implements FlywayConfigurationCustomizer {

    @Override
    public void customize(FluentConfiguration configuration) {
        // do something with configuration
    }
}

当命名了数据源时,@FlywayDataSource 注释可用来指定自定义程序应用其中的数据源。例如,如果有许多数据源,其中一个被称为 users,且仅针对该数据源需要对 Flyway 进行自定义,则可以使用以下代码:

When named datasources are used, the @FlywayDataSource annotation can be used to specify the datasource to which the customizer applies. For example, if there are multiple datasources one of which is called users and customization of Flyway is needed for only that datasource, then the following code can be used:

@Singleton
@FlywayDataSource("users")
public static class UsersCustomizer implements FlywayConfigurationCustomizer {

    @Override
    public void customize(FluentConfiguration configuration) {
        // do something with configuration
    }
}

Using the Flyway object

如果你有兴趣直接使用 Flyway 对象,你可以如下注入它:

In case you are interested in using the Flyway object directly, you can inject it as follows:

@ApplicationScoped
public class MigrationService {
    // You can Inject the object if you want to use it manually
    @Inject
    Flyway flyway; 1

    @Inject
    @FlywayDataSource("inventory") 2
    Flyway flywayForInventory;

    @Inject
    @Named("flyway_users") 3
    Flyway flywayForUsers;

    public void checkMigration() {
        // Use the flyway instance manually
        flyway.clean(); 4
        flyway.migrate();
        // This will print 1.0.0
        System.out.println(flyway.info().current().getVersion().toString());
    }
}
1 Inject the Flyway object if you want to use it directly
2 Inject Flyway for named datasources using the Quarkus FlywayDataSource qualifier
3 Inject Flyway for named datasources
4 Use the Flyway instance directly

Flyway and Hibernate ORM

当将 Flyway 与 Hibernate ORM 结合使用时,可以使用 Dev UI 来生成初始化架构创建脚本。

When using Flyway together with Hibernate ORM, you can use the Dev UI to generate the initial schema creation script.

你可以在 Hibernate ORM guide 中找到有关此功能的更多信息。

You can find more information about this feature in the Hibernate ORM guide.

Flyway and Reactive datasources

Flyway 在内部依赖 JDBC 数据源,而响应式用例将依靠 reactive SQL clients,直接使用或通过 Hibernate Reactive 使用。这在 Quarkus 中不是问题,因为 a single configured datasource can be made available both through reactive clients and JDBC

Flyway internally relies on a JDBC datasource, whereas reactive use cases will rely on reactive SQL clients, either directly or through Hibernate Reactive. This is not a problem in Quarkus, because a single configured datasource can be made available both through reactive clients and JDBC.

要在数据源(你通常以响应方式对其进行访问)上使用 Flyway,只需确保将该数据源配置为 JDBCreactive。特别是,这涉及向 Quarkus 扩展添加 JDBC 驱动程序和响应式客户端的依赖项,例如 quarkus-jdbc-postgresql and quarkus-reactive-pg-client

To use Flyway on a datasource you otherwise access reactively, simply make sure to configure that datasource both as JDBC and reactive. This involves in particular adding dependencies to Quarkus extensions for both the JDBC driver and the reactive client, for instance quarkus-jdbc-postgresql and quarkus-reactive-pg-client.

Flyway on Kubernetes

Sometimes, it’s helpful not to execute Flyway initialization on each application startup. One such example is when deploying

on Kubernetes, where it doesn’t make sense to execute Flyway on every single replica. Instead it’s desirable to execute itonce and then start the actual application without Flyway. To support this use case, when generating manifests for Kubernetesthe generated manifests contain a Kubernetes initialization Job for Flyway.The Job performs initialization and the actual Pod, will starts once the Job is successfully completed.

on Kubernetes, where it doesn’t make sense to execute Flyway on every single replica. Instead it’s desirable to execute it once and then start the actual application without Flyway. To support this use case, when generating manifests for Kubernetes the generated manifests contain a Kubernetes initialization Job for Flyway. The Job performs initialization and the actual Pod, will starts once the Job is successfully completed.

Disabling

此功能默认启用,可以使用以下内容进行全局禁用:

The feature is enabled by default and can be globally disabled, using:

quarkus.kubernetes.init-task-defaults.enabled=false

或在 OpenShift 上:

or on OpenShift:

quarkus.openshift.init-task-defaults.enabled=false

Using a custom image that controls waiting for the Job

若要更改默认`groundnuty/k8s-wait-for:no-root-v1.7` 的 wait-for 图像,可以使用:

To change the wait-for image which by default is groundnuty/k8s-wait-for:no-root-v1.7 you can use:

quarkus.kubernetes.init-task-defaults.wait-for-container.image=my/wait-for-image:1.0

或在 OpenShift 上:

or on OpenShift:

quarkus.openshift.init-task-defaults.wait-for-container.image=my/wait-for-image:1.0

Note: 在此上下文中,全局意味着 for all extensions that support init task externalization.

Note: In this context globally means for all extensions that support init task externalization.