可用版本:Dev (3.21) | 最新版 (3.20) | 3.19 | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11

将 jOOQ 与 Flyway 一起使用

适用于 ✅ 开源版   ✅ 专业版   ✅ 企业版

Flyway - 数据库迁移变得简单在执行数据库迁移时,Data Geekery 建议将 jOOQ 与 Flyway - 数据库迁移变得简单 一起使用。在本章中,我们将研究一种使用这两个框架的简单方法。

理念

jOOQ 和 Flyway 在各种开发设置中可以通过多种方式相互交互。在本教程中,我们将仅展示这种框架团队合作的一种变体——我们发现对于大多数用例来说特别引人注目的一种变体。

以下方法的总体理念可以概括如下

  • 1. 数据库增量
  • 2. 数据库迁移
  • 3. 代码重新生成
  • 4. 开发

每当您需要修改数据库中的某些内容时,都可以一次又一次地重复以上四个步骤。更具体地说,让我们考虑

  • 1. 数据库增量 - 您需要在数据库中添加一个新列,因此您在 Flyway 脚本中编写必要的 DDL
  • 2. 数据库迁移 - 此 Flyway 脚本现在是您的可交付成果的一部分,您可以与所有开发人员共享,下次他们检出您的更改时,他们可以使用它来迁移他们的数据库
  • 3. 代码重新生成 - 迁移数据库后,您可以在本地重新生成所有 jOOQ 工件(请参阅代码生成
  • 4. 开发 - 您继续开发业务逻辑,根据更新的、生成的数据库模式编写代码

Maven 项目配置 - 属性

以下属性在我们的 pom.xml 中定义,以便能够在插件配置之间重用它们

<properties>
    <db.url>jdbc:h2:~/flyway-test</db.url>
    <db.username>sa</db.username>
</properties>

0. Maven 项目配置 - 依赖项

虽然 jOOQ 和 Flyway 可以在独立的迁移脚本中使用,但在本教程中,我们将使用 Maven 进行标准项目设置。

这些是我们在 Maven 配置中使用的依赖项

<!-- We'll add the latest version of jOOQ and our JDBC driver - in this case H2 -->
<dependency>
                <!-- Use org.jooq                for the Open Source Edition
                         org.jooq.pro            for commercial editions with Java 21 support,
                         org.jooq.pro-java-17    for commercial editions with Java 17 support,
                         org.jooq.pro-java-11    for commercial editions with Java 11 support,
                         org.jooq.pro-java-8     for commercial editions with Java 8 support,
                         org.jooq.trial          for the free trial edition with Java 21 support,
                         org.jooq.trial-java-17  for the free trial edition with Java 17 support,
                         org.jooq.trial-java-11  for the free trial edition with Java 11 support,
                         org.jooq.trial-java-8   for the free trial edition with Java 8 support
    
                 Note: Only the Open Source Edition is hosted on Maven Central.
                       Install the others locally using the provided scripts, or access them from here: https://repo.jooq.org
                       See the JDK version support matrix here: https://jooq.org.cn/download/support-matrix-jdk -->

    <groupId>org.jooq</groupId>
    <artifactId>jooq</artifactId>
    <version>3.20.5</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.197</version>
</dependency>

<!-- For improved logging, we'll be using log4j via slf4j to see what's going on during migration and code generation -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.0</version>
</dependency>

<!-- To ensure our code is working, we're using JUnit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

0. Maven 项目配置 - 插件

在依赖项之后,让我们简单地添加 Flyway 和 jOOQ Maven 插件。Flyway 插件

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

    <!-- Note that we're executing the Flyway plugin in the "generate-sources" phase -->
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>migrate</goal>
            </goals>
        </execution>
    </executions>

    <!-- Note that we need to prefix the db/migration path with filesystem: to prevent Flyway
         from looking for our migration scripts only on the classpath -->
    <configuration>
        <url>${db.url}</url>
        <user>${db.username}</user>
        <locations>
            <location>filesystem:src/main/resources/db/migration</location>
        </locations>
    </configuration>
</plugin>

上面的 Flyway Maven 插件配置将读取并执行来自 src/main/resources/db/migration 的所有数据库迁移脚本,然后再编译 Java 源代码。虽然官方 Flyway 文档可能建议在 compile 阶段进行迁移,但 jOOQ 代码生成器依赖于此类迁移在代码生成之前完成。

在 Flyway 插件之后,我们将添加 jOOQ Maven 插件。有关更多详细信息,请参阅手册中有关代码生成配置的部分

<plugin>
                <!-- Use org.jooq                for the Open Source Edition
                         org.jooq.pro            for commercial editions with Java 21 support,
                         org.jooq.pro-java-17    for commercial editions with Java 17 support,
                         org.jooq.pro-java-11    for commercial editions with Java 11 support,
                         org.jooq.pro-java-8     for commercial editions with Java 8 support,
                         org.jooq.trial          for the free trial edition with Java 21 support,
                         org.jooq.trial-java-17  for the free trial edition with Java 17 support,
                         org.jooq.trial-java-11  for the free trial edition with Java 11 support,
                         org.jooq.trial-java-8   for the free trial edition with Java 8 support
    
                 Note: Only the Open Source Edition is hosted on Maven Central.
                       Install the others locally using the provided scripts, or access them from here: https://repo.jooq.org
                       See the JDK version support matrix here: https://jooq.org.cn/download/support-matrix-jdk -->

    <groupId>org.jooq</groupId>
    <artifactId>jooq-codegen-maven</artifactId>
    <version>${org.jooq.version}</version>

    <!-- The jOOQ code generation plugin is also executed in the generate-sources phase, prior to compilation -->
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>

    <!-- This is a minimal working configuration. See the manual's section about the code generator for more details -->
    <configuration>
        <jdbc>
            <url>${db.url}</url>
            <user>${db.username}</user>
        </jdbc>
        <generator>
            <database>
                <includes>.*</includes>
                <inputSchema>FLYWAY_TEST</inputSchema>
            </database>
            <target>
                <packageName>org.jooq.example.flyway.db.h2</packageName>
                <directory>target/generated-sources/jooq-h2</directory>
            </target>
        </generator>
    </configuration>
</plugin>

此配置现在将读取 FLYWAY_TEST 模式并将其反向工程到 target/generated-sources/jooq-h2 目录中,并在其中反向工程到 org.jooq.example.flyway.db.h2 包中。

1. 数据库增量

现在,当我们开始开发我们的数据库时。为此,我们将创建数据库增量脚本,我们将其放入 src/main/resources/db/migration 目录中,如先前为 Flyway 插件配置的那样。我们将添加以下文件

  • V1__initialise_database.sql
  • V2__create_author_table.sql
  • V3__create_book_table_and_records.sql

这三个脚本模拟了我们的模式版本 1-3(注意大写 V!)。以下是脚本的内容

-- V1__initialise_database.sql
DROP SCHEMA flyway_test IF EXISTS;

CREATE SCHEMA flyway_test;
-- V2__create_author_table.sql
CREATE SEQUENCE flyway_test.s_author_id START WITH 1;

CREATE TABLE flyway_test.author (
  id INT NOT NULL,
  first_name VARCHAR(50),
  last_name VARCHAR(50) NOT NULL,
  date_of_birth DATE,
  year_of_birth INT,
  address VARCHAR(50),

  CONSTRAINT pk_author PRIMARY KEY (ID)
);
-- V3__create_book_table_and_records.sql
CREATE TABLE flyway_test.book (
  id INT NOT NULL,
  author_id INT NOT NULL,
  title VARCHAR(400) NOT NULL,

  CONSTRAINT pk_book PRIMARY KEY (id),
  CONSTRAINT fk_book_author_id FOREIGN KEY (author_id) REFERENCES flyway_test.author(id)
);


INSERT INTO flyway_test.author VALUES (next value for flyway_test.s_author_id, 'George', 'Orwell', '1903-06-25', 1903, null);
INSERT INTO flyway_test.author VALUES (next value for flyway_test.s_author_id, 'Paulo', 'Coelho', '1947-08-24', 1947, null);

INSERT INTO flyway_test.book VALUES (1, 1, '1984');
INSERT INTO flyway_test.book VALUES (2, 1, 'Animal Farm');
INSERT INTO flyway_test.book VALUES (3, 2, 'O Alquimista');
INSERT INTO flyway_test.book VALUES (4, 2, 'Brida');

2. 数据库迁移和 3. 代码重新生成

以上三个脚本由 Flyway 拾取并按版本顺序执行。这可以通过执行以下操作非常简单地看到

mvn clean install

然后观察来自 Flyway 的日志输出...

[INFO] --- flyway-maven-plugin:3.0:migrate (default) @ jooq-flyway-example ---
[INFO] Database: jdbc:h2:~/flyway-test (H2 1.4)
[INFO] Validated 3 migrations (execution time 00:00.004s)
[INFO] Creating Metadata table: "PUBLIC"."schema_version"
[INFO] Current version of schema "PUBLIC": << Empty Schema >>
[INFO] Migrating schema "PUBLIC" to version 1
[INFO] Migrating schema "PUBLIC" to version 2
[INFO] Migrating schema "PUBLIC" to version 3
[INFO] Successfully applied 3 migrations to schema "PUBLIC" (execution time 00:00.073s).

...以及来自 jOOQ 在控制台上的日志输出

[INFO] --- jooq-codegen-maven:3.20.5:generate (default) @ jooq-flyway-example ---
[INFO] --- jooq-codegen-maven:3.20.5:generate (default) @ jooq-flyway-example ---
[INFO] Using this configuration:
...
[INFO] Generating schemata      : Total: 1
[INFO] Generating schema        : FlywayTest.java
[INFO] ----------------------------------------------------------
[....]
[INFO] GENERATION FINISHED!     : Total: 337.576ms, +4.299ms

4. 开发

请注意,每次有人向 Maven 模块添加新的迁移脚本时,所有先前的步骤都会自动执行。例如,团队成员可能提交了一个新的迁移脚本,您将其检出,重建并获取最新的 jOOQ 生成的源,用于您自己的开发或集成测试数据库。

现在,这些步骤已完成,您可以继续编写数据库查询。想象一下以下测试用例

import org.jooq.Result;
import org.jooq.impl.DSL;
import org.junit.Test;

import java.sql.DriverManager;

import static java.util.Arrays.asList;
import static org.jooq.example.flyway.db.h2.Tables.*;
import static org.junit.Assert.assertEquals;

public class AfterMigrationTest {

    @Test
    public void testQueryingAfterMigration() throws Exception {
        try (Connection c = DriverManager.getConnection("jdbc:h2:~/flyway-test", "sa", "")) {
            Result<?> result =
            DSL.using(c)
               .select(
                   AUTHOR.FIRST_NAME,
                   AUTHOR.LAST_NAME,
                   BOOK.ID,
                   BOOK.TITLE
               )
               .from(AUTHOR)
               .join(BOOK)
               .on(AUTHOR.ID.eq(BOOK.AUTHOR_ID))
               .orderBy(BOOK.ID.asc())
               .fetch();

            assertEquals(4, result.size());
            assertEquals(asList(1, 2, 3, 4), result.getValues(BOOK.ID));
        }
    }
}

迭代

一旦您开始以这种方式执行数据库修改,这种方法的力量就会变得清晰起来。让我们假设我们团队中的法国人更喜欢按照自己的方式做事

-- V4__le_french.sql
ALTER TABLE flyway_test.book ALTER COLUMN title RENAME TO le_titre;

他们将其检入,您检出新的数据库迁移脚本,运行

mvn clean install

然后观察日志输出

[INFO] --- flyway-maven-plugin:3.0:migrate (default) @ jooq-flyway-example ---
[INFO] --- flyway-maven-plugin:3.0:migrate (default) @ jooq-flyway-example ---
[INFO] Database: jdbc:h2:~/flyway-test (H2 1.4)
[INFO] Validated 4 migrations (execution time 00:00.005s)
[INFO] Current version of schema "PUBLIC": 3
[INFO] Migrating schema "PUBLIC" to version 4
[INFO] Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.016s).

到目前为止一切顺利,但是后来

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] C:\...\jOOQ-flyway-example\src\test\java\AfterMigrationTest.java:[24,19] error: cannot find symbol
[INFO] 1 error

当我们回到我们的 Java 集成测试时,我们可以立即看到仍然引用了 TITLE 列,但它不再存在

public class AfterMigrationTest {

    @Test
    public void testQueryingAfterMigration() throws Exception {
        try (Connection c = DriverManager.getConnection("jdbc:h2:~/flyway-test", "sa", "")) {
            Result<?> result =
            DSL.using(c)
                .select(
                    AUTHOR.FIRST_NAME,
                    AUTHOR.LAST_NAME,
                    BOOK.ID,
                    BOOK.TITLE
                    //   ^^^^^ This column no longer exists. We'll have to rename it to LE_TITRE
                )
                .from(AUTHOR)
                .join(BOOK)
                .on(AUTHOR.ID.eq(BOOK.AUTHOR_ID))
                .orderBy(BOOK.ID.asc())
                .fetch();

            assertEquals(4, result.size());
            assertEquals(asList(1, 2, 3, 4), result.getValues(BOOK.ID));
        }
    }
}

自动化

可以使用另一个称为 testcontainers 的第三方在构建中自动化上述步骤。请查看此处的文章,了解如何执行此操作的示例:https://blog.jooq.org/using-testcontainers-to-generate-jooq-code/

结论

本教程非常容易地展示了如何使用 Flyway 和 jOOQ 构建可靠的开发流程,以便在开发的早期阶段(立即在编译时)而不是在生产中防止与 SQL 相关的错误!

请访问 Flyway 网站 了解有关 Flyway 的更多信息。

反馈

您对此页面有任何反馈吗?我们很乐意听到您的反馈!

The jOOQ Logo