Перевод статьи подготовлен в преддверии старта курса «Разработчик Java».




1. Введение


В этой статье описываются ключевые концепции Flyway и пример использования этого фреймворка для непрерывного изменения схемы базы данных на примере in-memory базы данных H2 с помощью maven-плагина flyway.

Flyway обновляет версии баз данных с помощью миграций. Миграции можно писать на SQL (с синтаксисом, специфичным для конкретной СУБД) или на Java.

Миграции могут быть версионными или повторяющимися. Первые имеют уникальную версию и применяются ровно один раз. У вторых номера версии нет, и они применяются, когда у них изменяется контрольная сумма.

Повторяющиеся миграции в рамках одного запуска всегда применяются после выполнения версионных миграций. Повторяющиеся миграции применяются в порядке их описания. В одной миграции все операции выполняются в рамках одной транзакции базы данных.

В этой статье мы сосредоточим внимание на использовании maven-плагина для миграций базы данных.

2. Flyway maven plugin


Добавим flyway maven plugin в pom.xml:

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

Актуальную версию плагина можно посмотреть в Maven Central.
Список параметров плагина, можно посмотреть в документации. Параметры плагина можно настроить четырьмя различными способами.

2.1. Раздел <configuration> плагина
Параметры можно указать напрямую в теге в разделе плагина в pom.xml:

<plugin>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-maven-plugin</artifactId>
    <version>4.0.3</version>
    <configuration>
        <user>databaseUser</user>
        <password>databasePassword</password>
        <schemas>
            <schema>schemaName</schema>
        </schemas>
        ...
    </configuration>
</plugin>

2.2. Maven properties


Также плагин можно настроить, указав параметры в <properties>:

<project>
    ...
    <properties>
        <flyway.user>databaseUser</flyway.user>
        <flyway.password>databasePassword</flyway.password>
        <flyway.schemas>schemaName</flyway.schemas>
        ...
    </properties>
    ...
</project>

2.3. Внешний файл конфигурации


Или описать конфигурацию в отдельном .properties-файле:

flyway.user=databaseUser
flyway.password=databasePassword
flyway.schemas=schemaName
...

По умолчанию имя файла конфигурации flyway.properties. Этот файл должен находиться в том же каталоге, что и файл pom.xml. Кодировка задается в параметре flyway.encoding (по умолчанию UTF-8).

Если для файла вы используете другое имя (например, customConfig.properties), то его нужно указать явно при вызове maven:

$ mvn -Dflyway.configFile=customConfig.properties

2.4. System Properties


И наконец, параметры могут быть указаны как system properties при вызове maven из командной строки:

$ mvn -Dflyway.user=databaseUser -Dflyway.password=databasePassword
  -Dflyway.schemas=schemaName

Если конфигурация указана несколькими способами, то приоритет будет следующий:

  1. System properties
  2. Внешний файл конфигурации
  3. Раздел <properties>
  4. Раздел <configuration> плагина

3. Пример миграции


В этом разделе мы рассмотрим необходимые шаги для миграции схемы базы данных на примере in-memory базы данных H2 с помощью maven-плагина. Для конфигурации Flyway мы будем использовать внешний файл.

3.1. Изменения в POM


Для начала, добавим зависимость на H2:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

Здесь мы также можем проверить последнюю доступную версию драйвера в Maven Central. Плагин для Flyway добавляем, как было описано ранее.

3.2. Настройка Flyway во внешнем файле


Создаем в $PROJECT_ROOT файл myFlywayConfig.properties со следующим содержимым:

flyway.user=databaseUser
flyway.password=databasePassword
flyway.schemas=app-db
flyway.url=jdbc:h2:mem:DATABASE
flyway.locations=filesystem:db/migration

Приведенная выше конфигурация указывает, что скрипты миграции находятся в каталоге db/migration, а для подключения к базе данных H2 используются databaseUser и databasePassword.

Схема базы данных для приложения — app-db.

Конечно, в параметрах flyway.user, flyway.password и flyway.url необходимо указать имя, пароль и URL вашей базы данных.

3.3. Первая миграция


По соглашениям Flyway имена скриптов миграции должны быть в следующем формате:

<Prefix<code>><</code>Version>__<code><</code>Description>.sql

Где:

  • <Prefix> — префикс. Для версионных миграций по умолчанию равен “V”. Префикс настраивается через свойство flyway.sqlMigrationPrefix.
  • <Version> — номер версии миграции. Мажорную и минорную версии можно разделить подчеркиванием. Версия всегда должна начинаться с 1.
  • <Description> — текстовое описание миграции. Описание должно быть отделено от номера версии двумя подчеркиваниями.

Пример: V1_1_0__my_first_migration.sql

Итак, давайте создадим каталог db/migration в $PROJECT_ROOT со скриптом миграции V1_0__create_employee_schema.sql и SQL для создания таблицы employee:

CREATE TABLE IF NOT EXISTS `employee` (
 
    `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `name` varchar(20),
    `email` varchar(50),
    `date_of_birth` timestamp
 
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

3.4. Выполняем миграции


Далее, в $PROJECT_ROOT запускаем следующую команду maven для применения миграций базы данных:

$ mvn clean flyway:migrate -Dflyway.configFile=myFlywayConfig.properties

Должна выполниться наша первая миграция.

Теперь схема базы данных выглядит следующим образом:
employee:

+----+------+-------+---------------+
| id | name | email | date_of_birth |
+----+------+-------+---------------+

Мы можем повторить предыдущие шаги для выполнения других миграций.

3.5. Вторая миграция


Для второй миграции создаем файл с именем V2_0_create_department_schema.sql, содержащий следующие два запроса:

CREATE TABLE IF NOT EXISTS `department` (
 
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(20)
 
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
 
ALTER TABLE `employee` ADD `dept_id` int AFTER `email`;

Выполним миграцию, также как делали для первой миграции.

Теперь схема нашей базы данных изменилась: в employee добавлен новый столбец и создана новая таблица department:

employee:
+----+------+-------+---------+---------------+
| id | name | email | dept_id | date_of_birth |
+----+------+-------+---------+---------------+

department:

+----+------+
| id | name |
+----+------+

Для проверки, что обе миграции прошли успешно, запустим следующую команду maven:

$ mvn flyway:info -Dflyway.configFile=myFlywayConfig.properties

4. Отключение Flyway в Spring Boot


Иногда может потребоваться отключить Flyway-миграции.

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

В Spring Boot это сделать очень просто.

4.1. Spring Boot 1.x


Все, что нам нужно сделать, это установить свойство flyway.enabled в файле application-test.properties:

flyway.enabled=false


4.2. Spring Boot 2.x


В более поздних версиях Spring Boot это свойство было изменено на

spring.flyway.enabled:
spring.flyway.enabled=false

4.3 Пустая FlywayMigrationStrategy


Если мы хотим отключить только автоматическую миграцию Flyway при запуске, но хотим запускать миграции вручную, то использование вышеописанных свойств нам не подойдет.

Это связано с тем, что Spring Boot не будет автоматически конфигурировать бины Flyway и, следовательно, нам придется настраивать их самостоятельно, что не очень удобно.

В этом случае мы можем оставить Flyway включенным и реализовать пустую FlywayMigrationStrategy:

@Configuration
public class EmptyMigrationStrategyConfig {
 
    @Bean
    public FlywayMigrationStrategy flywayMigrationStrategy() {
        return flyway -> {
            // do nothing 
        };
    }
}

Фактически это отключит Flyway-миграции при запуске приложения.

Но мы все равно можем запускать миграции вручную:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ManualFlywayMigrationIntegrationTest {
 
    @Autowired
    private Flyway flyway;
 
    @Test
    public void skipAutomaticAndTriggerManualFlywayMigration() {
        flyway.migrate();
    }
}

5. Как работает Flyway


Для отслеживания когда, кем и какие миграции были применены, в схему базы данных добавляется специальная таблица с метаданными. В этой таблице также хранятся контрольные суммы миграций и информация о том успешна была миграция или нет.

Фреймворк работает следующим образом:

  1. Проверяет схему базы данных на наличие таблицы метаданных (по умолчанию SCHEMA_VERSION). Если таблица метаданных не существует, то создает ее.
  2. Сканирует classpath на наличие доступных миграций.
  3. Сравнивает миграции с таблицей метаданных. Если номер версии меньше или равен версии, помеченной как текущая, то игнорирует ее.
  4. Отмечает все оставшиеся миграции как ожидающие (pending). Потом сортирует их по возрастанию номеров версий и выполняет в указанном порядке.
  5. По мере применения миграций обновляет таблицу метаданных.

6. Команды


В Flyway есть следующие основные команды по управлению миграциями:

  • Info. Отображение текущего состояния / версии схемы базы данных. Информация о том, какие миграции ожидаются, какие были применены, состояние выполненных миграций и дата их выполнения.
  • Migrate. Обновление схемы базы данных до текущей версии. Сканирование classpath для поиска доступных миграций и применение ожидающих миграций.
  • Baseline. Установка версии схемы базы данных, игнорируя миграции до baselineVersion включительно. Baseline помогает использовать Flyway на уже существующей базе данных. Новые миграции применяются в обычном режиме.
  • Validate. Проверка текущей схемы базы данных на соответствие доступным миграциям.
  • Repair. Восстановление таблицы метаданных.
  • Clean. Удаление всех объектов в схеме. Конечно, никогда не нужно использовать clean в продакшен базах данных.

7. Заключение


  • В этой статье мы показали, как работает Flyway и как его можно использовать для надежного и простого управления изменениями базы данных.
  • Код статьи доступен на GitHub.



УПРАВЛЯЕМ ВЕРСИЯМИ БАЗЫ ДАННЫХ ЧЕРЕЗ FLYWAY