Миграции баз данных в Kotlin: Полное руководство по инструментам и подходам
Управление изменениями схемы базы данных — одна из ключевых задач при разработке любого приложения. В экосистеме Kotlin существует несколько зрелых решений, которые позволяют автоматизировать этот процесс. В этой статье мы разберем три основных подхода: встроенные средства ORM Exposed, классический Flyway и мощный Liquibase. Вы узнаете, как интегрировать их в проект на Kotlin и какой инструмент выбрать под конкретные задачи.
1. Зачем нужны миграции?
Ручное выполнение SQL-скриптов на разных окружениях (локальная среда разработки, стейджинг, продакшн) неизбежно приводит к рассинхронизации схемы. Миграции решают эту проблему, предоставляя:
- Версионирование изменений базы данных
- Автоматическое применение новых скриптов при старте приложения
- Возможность отката (rollback) до предыдущей версии
- Командную работу без конфликтов схемы
2. Exposed: Нативная поддержка миграций
Exposed — это легковесная ORM от JetBrains, написанная на Kotlin. Начиная с версии 0.41.0, библиотека предоставляет собственный механизм миграций, который идеально вписывается в экосистему Kotlin.
Установка
// build.gradle.kts
implementation("org.jetbrains.exposed:exposed-core:0.55.0")
implementation("org.jetbrains.exposed:exposed-dao:0.55.0")
implementation("org.jetbrains.exposed:exposed-migrations:0.55.0")
Создание миграции
Каждая миграция — это класс, наследующий от VoidDatabaseMigration:
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.migrations.VoidDatabaseMigration
object Users : Table() {
val id = integer("id").autoIncrement()
val name = varchar("name", 255)
override val primaryKey = PrimaryKey(id)
}
class V1__CreateUsersTable : VoidDatabaseMigration() {
override fun migrate() {
transaction {
SchemaUtils.create(Users)
}
}
}
class V2__AddEmailColumn : VoidDatabaseMigration() {
override fun migrate() {
transaction {
SchemaUtils.addColumn(Users, Users.varchar("email", 255))
}
}
}
Запуск миграций
import org.jetbrains.exposed.migrations.ExposedMigrationUtils
import javax.sql.DataSource
fun runMigrations(dataSource: DataSource) {
ExposedMigrationUtils.runMigrations(
dataSource = dataSource,
migrationsPackage = "com.example.migrations"
)
}
Плюсы: полностью Kotlin-стиль, нет SQL-файлов, типизированные имена колонок.
Минусы: привязка к Exposed, нет визуального трекера, отсутствие rollback "из коробки".
3. Flyway: Простота и надёжность
Flyway — самый популярный инструмент для миграций в JVM-мире. Он работает с любыми базами данных и не зависит от ORM. В Kotlin его используют через официальный SDK или Gradle-плагин.
Подключение
// build.gradle.kts
implementation("org.flywaydb:flyway-core:10.20.0")
// Для PostgreSQL добавьте драйвер:
implementation("org.postgresql:postgresql:42.7.4")
Структура миграций
Flyway использует SQL-файлы с определённым именованием:
db/migration/
V1__create_users.sql
V2__add_email_column.sql
V3__add_role_column.sql
Пример SQL-миграции (V1__create_users.sql)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE
);
Интеграция с Kotlin
import org.flywaydb.core.Flyway
import javax.sql.DataSource
fun migrateDatabase(dataSource: DataSource) {
val flyway = Flyway.configure()
.dataSource(dataSource)
.locations("classpath:db/migration")
.load()
flyway.migrate()
}
Плюсы: простота, поддержка откатов (через V1__...__description.sql и U1__...sql), интеграция с CI/CD.
Минусы: SQL-файлы вне Kotlin-кода, сложно рефакторить имена колонок.