Статьи

Flyway и jOOQ для непревзойденной производительности разработки SQL

 

При выполнении миграции баз данных мы в Data Geekery рекомендуем использовать jOOQ с Flyway — Миграция баз данных Made Easy . В этом посте мы рассмотрим простой способ начать работу с двумя фреймворками.

философия

Существует множество способов взаимодействия jOOQ и Flyway друг с другом в различных настройках разработки. В этом уроке мы покажем только один вариант такой командной игры фреймворка — вариант, который мы находим особенно привлекательным для большинства случаев использования.

Общая философия и рабочий процесс, лежащий в основе следующего подхода, могут быть обобщены следующим образом:

  • 1. Приращение базы данных
  • 2. Миграция базы данных
  • 3. Повторная генерация кода
  • 4. Разработка

Четыре вышеуказанных шага можно повторять снова и снова, каждый раз, когда вам нужно что-то изменить в вашей базе данных. Конкретнее, давайте рассмотрим:

  • 1. Приращение базы данных  — вам нужен новый столбец в вашей базе данных, поэтому вы пишете необходимый DDL в скрипте Flyway
  • 2. Перенос базы данных.  Этот сценарий Flyway теперь является частью вашего конечного результата, которым вы можете поделиться со всеми разработчиками, которые могут перенести свои базы данных вместе с ним, при следующей проверке ваших изменений.
  • 3. Повторная генерация кода  — после переноса базы данных вы регенерируете все артефакты jOOQ (см.  Генерацию кода ) локально
  • 4. Разработка.  Вы продолжаете разрабатывать свою бизнес-логику, писать код для обновленной схемы базы данных.

0,1. Конфигурация проекта Maven — Свойства

Следующие свойства определены в нашем pom.xml, чтобы можно было повторно использовать их между конфигурациями плагинов:

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

0.2. Конфигурация проекта Maven — Зависимости

Хотя jOOQ и Flyway можно использовать в автономных сценариях миграции, в этом руководстве мы будем использовать Maven для стандартной настройки проекта. Вы также найдете исходный код этого руководства на GitHub и полный  файл pom.xml здесь .

Это зависимости, которые мы используем в нашей конфигурации Maven:

<!-- We'll add the latest version of jOOQ
     and our JDBC driver - in this case H2 -->
<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>jooq</artifactId>
    <version>3.4.0</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.177</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>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.16</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.5</version>
</dependency>
 
<!-- To esnure our code is working, we're
     using JUnit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

0,3. Конфигурация проекта 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>
    <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_t_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_t_book PRIMARY KEY (id),
  CONSTRAINT fk_t_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": <>
[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.5.0-SNAPSHOT: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));
        }
    }
}

Если вы запустите mvn clean installснова, вышеприведенный тест интеграции будет скомпилирован и пройден!

Подтверждают

Сила этого подхода становится очевидной, как только вы начнете выполнять модификации базы данных таким способом. Давайте предположим, что французский парень в нашей команде предпочитает, чтобы все было по-своему (без обид;)):

-- 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:\...\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));
        }
    }
}

Вывод

Это руководство очень просто показывает, как вы можете построить надежный процесс разработки с использованием Flyway и jOOQ для предотвращения ошибок, связанных с SQL, на самых ранних этапах жизненного цикла разработки — сразу во время компиляции, а не в процессе производства!

Посетите  веб — сайт пролетного пути и сайт jOOQ .