Миграции
Миграции базы данных применяют изменения схемы и справочных данных в контролируемом порядке: они создают таблицы, индексы, ограничения и выполняют другие операции SQL, необходимые новой версии приложения.
В Kora модули миграций привязаны к инициализации JdbcDatabase через GraphInterceptor<JdbcDatabase>: при запуске приложения JdbcDatabase создается как компонент графа, а метод init() перехватчика выполняет миграции до того, как компонент будет опубликован для остальной части графа.
Если миграция завершается ошибкой, метод init() выбрасывает исключение, поэтому инициализация компонента JdbcDatabase и построение всего графа (запуск приложения) также завершаются неудачей.
Метод release() перехватчика ничего не делает: миграции никогда не откатываются и не выполняются повторно при остановке приложения.
Такой подход удобен для локальной разработки, тестов и небольших установок, где приложение запускается в одном экземпляре. Для окружений с несколькими репликами заранее выберите отдельный способ выполнения миграций, чтобы они не запускались одновременно из каждого экземпляра приложения. Репозитории не создают схему базы данных сами: таблицы, индексы, ограничения и справочные данные должны создаваться миграциями или внешним процессом подготовки базы данных.
Flyway¶
Модуль для миграции базы данных с помощью инструмента Flyway.
При инициализации JdbcDatabase модуль вызывает Flyway.migrate() с настройками из секции flyway.
Миграции запускает FlywayJdbcDatabaseInterceptor, который предоставляется модулем FlywayJdbcDatabaseModule.
Flyway подключен к SLF4J (loggers("slf4j")), поэтому вывод миграций и строка с замером времени FlyWay migration applied in ... (журналируемая на уровне INFO) попадают в обычные логи приложения.
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Требует подключения JDBC-модуля, так как миграции выполняются через DataSource.
В приложении обычно подключаются оба модуля: JdbcDatabaseModule создает JdbcDatabase, а FlywayJdbcDatabaseModule добавляет перехватчик миграций.
Конфигурация¶
Пример полной конфигурации, описанной в классе FlywayConfig:
flyway {
enabled = true //(1)!
locations = ["db/migration"] //(2)!
executeInTransaction = true //(3)!
validateOnMigrate = true //(4)!
mixed = false //(5)!
configurationProperties {} //(6)!
}
- Включает выполнение миграций при инициализации
JdbcDatabase(по умолчанию:true). Если указатьfalse, модуль пропустит вызовFlyway.migrate(). - Пути к директориям со скриптами миграций (по умолчанию:
["db/migration"]). - Выполняет миграции внутри транзакции, если это поддерживается базой данных и самими операциями
SQL(по умолчанию:true). - Проверяет контрольные суммы уже примененных миграций перед выполнением новых (по умолчанию:
true). Если контрольные суммы не совпадают, запуск завершится ошибкой. - Разрешает смешивать транзакционные и нетранзакционные операции
SQLв одной миграции (по умолчанию:false). Если настройка включена, вся миграция выполняется без транзакции, чтобы избежать ошибок в базах данных, где часть операций нельзя выполнять внутри транзакции. Настройка актуальна для баз данных, которые не поддерживают выполнение отдельных операций внутри транзакции: PostgreSQL, Aurora PostgreSQL, SQL Server и SQLite. - Дополнительные свойства
Flywayв формате ключ-значение (по умолчанию:{}). Через них можно передать настройки, у которых нет отдельной опции конфигурации Kora, напримерschemas,baselineOnMigrate,placeholderReplacementилиplaceholders.*.
flyway:
enabled: true #(1)!
locations: ["db/migration"] #(2)!
executeInTransaction: true #(3)!
validateOnMigrate: true #(4)!
mixed: false #(5)!
configurationProperties: {} #(6)!
- Включает выполнение миграций при инициализации
JdbcDatabase(по умолчанию:true). Если указатьfalse, модуль пропустит вызовFlyway.migrate(). - Пути к директориям со скриптами миграций (по умолчанию:
["db/migration"]). - Выполняет миграции внутри транзакции, если это поддерживается базой данных и самими операциями
SQL(по умолчанию:true). - Проверяет контрольные суммы уже примененных миграций перед выполнением новых (по умолчанию:
true). Если контрольные суммы не совпадают, запуск завершится ошибкой. - Разрешает смешивать транзакционные и нетранзакционные операции
SQLв одной миграции (по умолчанию:false). Если настройка включена, вся миграция выполняется без транзакции, чтобы избежать ошибок в базах данных, где часть операций нельзя выполнять внутри транзакции. Настройка актуальна для баз данных, которые не поддерживают выполнение отдельных операций внутри транзакции: PostgreSQL, Aurora PostgreSQL, SQL Server и SQLite. - Дополнительные свойства
Flywayв формате ключ-значение (по умолчанию:{}). Через них можно передать настройки, у которых нет отдельной опции конфигурации Kora, напримерschemas,baselineOnMigrate,placeholderReplacementилиplaceholders.*.
Файлы миграций¶
По умолчанию Flyway ищет миграции в src/main/resources/db/migration.
Обычный файл миграции имеет имя вида V1__init_schema.sql, где V1 — версия, а часть после двойного подчеркивания — описание.
Пример простой миграции:
При запуске Flyway создает служебную таблицу истории миграций и применяет только новые версии.
Если включена проверка validateOnMigrate, уже примененные файлы нельзя менять без отдельного процесса исправления истории миграций.
Liquibase¶
Модуль для миграции базы данных с помощью инструмента Liquibase.
При инициализации JdbcDatabase модуль получает соединение из DataSource, создает экземпляр Liquibase и вызывает update().
Миграции запускает LiquibaseJdbcDatabaseInterceptor, который предоставляется модулем LiquibaseJdbcDatabaseModule.
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Требует подключения JDBC-модуля, так как миграции выполняются через DataSource.
В приложении обычно подключаются оба модуля: JdbcDatabaseModule создает JdbcDatabase, а LiquibaseJdbcDatabaseModule добавляет перехватчик миграций.
Конфигурация¶
Пример полной конфигурации, описанной в классе LiquibaseConfig:
В отличие от Flyway, у модуля Liquibase нет настройки enabled: если модуль подключен к графу приложения, миграции запускаются при инициализации JdbcDatabase.
Если миграция Liquibase завершается ошибкой, модуль оборачивает ее в IllegalStateException, и запуск приложения прерывается.
Файлы миграций¶
По умолчанию Liquibase ищет основной файл changelog в src/main/resources/db/changelog/db.changelog-master.xml.
Liquibase поддерживает разные форматы changelog, но в SQL-ориентированном проекте часто удобнее хранить миграции в форматированном SQL.
Основной файл может подключать такие миграции через include.
Минимальный основной changelog:
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<include file="db/changelog/changes/001-init-users.sql"/>
</databaseChangeLog>
Пример подключенной миграции в форматированном SQL:
--liquibase formatted sql
--changeset app:001-init-users
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL
);
Рекомендации¶
Рекомендация
Модули миграций не рекомендуется использовать для выполнения миграций на старте приложения в горизонтально масштабируемых окружениях, где приложение запускается в нескольких репликах. Каждая реплика будет пытаться выполнить миграции при запуске. Также учитывайте, что каждый перезапуск приложения снова приводит к запуску механизма миграций.
В таких случаях для локальной разработки используйте Flyway Gradle Plugin,
для тестов — запуск Flyway из кода после старта базы данных,
для промышленного окружения Kubernetes — Kubernetes Job,
либо выполняйте миграции отдельно из CI.