spring-bootkotlingradleflywaybases-de-datosmigraciones
En muchos proyectos, especialmente en etapas iniciales, es común modificar el esquema de la base de datos directamente desde herramientas gráficas o scripts sueltos sin control de versiones. Esto funciona hasta que deja de funcionar: lo que está en tu local no coincide con staging, staging no coincide con producción, y nadie sabe quién agregó esa columna que rompe el deploy del viernes.
Flyway resuelve esto versionando tu esquema de base de datos con scripts SQL numerados. Cada cambio es un archivo, cada archivo tiene un orden, y Flyway se encarga de aplicar solo los que faltan en cada ambiente. En este tutorial lo vamos a integrar desde cero en un proyecto con Spring Boot, Kotlin y Gradle.
El problema sin migraciones
Sin una herramienta de migraciones, los problemas más comunes son:
- Desincronización entre entornos: lo que funciona en local falla en staging o producción
- Dificultad para reproducir bugs porque los esquemas son distintos
- Falta de trazabilidad: no sabes cuándo ni por qué se hizo un cambio
- Riesgo de perder datos en despliegues manuales
- Onboarding lento: cada nuevo desarrollador tiene que armar su base de datos a mano
Paso 1: Agrega Flyway a tu proyecto
En tu
build.gradle.kts, agrega la dependencia de Flyway y el driver de tu base de datos:kotlin
dependencies {
implementation("org.flywaydb:flyway-core")
implementation("org.flywaydb:flyway-database-postgresql") // módulo específico para PostgreSQL
runtimeOnly("org.postgresql:postgresql")
}Flyway 10+
A partir de Flyway 10, necesitas agregar el módulo específico de tu base de datos (por ejemplo,
flyway-database-postgresql, flyway-database-mysql). En versiones anteriores solo bastaba con flyway-core.Paso 2: Configura la conexión
En tu
application.yml:yaml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: myuser
password: secret
flyway:
enabled: true
locations: classpath:db/migrationCon
flyway.enabled: true, Spring Boot ejecutará las migraciones automáticamente al levantar la aplicación, antes de que JPA/Hibernate toque la base de datos.Paso 3: Crea tus migraciones
Flyway busca los scripts en
src/main/resources/db/migration. Cada archivo sigue la convención:V<versión>__<descripción>.sql
El doble guión bajo (
__) separa la versión de la descripción. La versión debe ser secuencial y única.V1__create_users_table.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT now()
);V2__create_orders_table.sql
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
total DECIMAL(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_orders_user_id ON orders(user_id);V3__add_phone_to_users.sql
ALTER TABLE users ADD COLUMN phone VARCHAR(20);Paso 4: Ejecuta y verifica
Al levantar tu aplicación, Flyway detecta las migraciones pendientes y las aplica en orden. En los logs vas a ver algo como esto:
text
Flyway Community Edition 10.x
Database: jdbc:postgresql://localhost:5432/mydb (PostgreSQL 16.x)
Successfully validated 3 migrations (execution time 00:00.028s)
Creating Schema History table "public"."flyway_schema_history" ...
Current version of schema "public": << Empty Schema >>
Migrating schema "public" to version "1 - create users table"
Migrating schema "public" to version "2 - create orders table"
Migrating schema "public" to version "3 - add phone to users"
Successfully applied 3 migrations to schema "public" (execution time 00:00.156s)Si vuelves a levantar la app sin nuevas migraciones, Flyway simplemente valida y no hace nada:
text
Successfully validated 3 migrations (execution time 00:00.032s)
Current version of schema "public": 3La tabla flyway_schema_history
Flyway crea automáticamente una tabla llamada
flyway_schema_history donde registra cada migración aplicada: versión, nombre del script, checksum, fecha, y tiempo de ejecución.sql
SELECT version, description, checksum, installed_on, success
FROM flyway_schema_history
ORDER BY version;text
version | description | checksum | installed_on | success
--------+----------------------+------------+---------------------+--------
1 | create users table | -817240893 | 2026-03-24 10:15:03 | true
2 | create orders table | 1042587231 | 2026-03-24 10:15:03 | true
3 | add phone to users | -293847561 | 2026-03-24 10:15:04 | trueEl checksum es un hash del contenido del archivo SQL. Si alguien modifica un script que ya fue aplicado, Flyway detecta la inconsistencia y se niega a levantar la aplicación. Esto es intencional: una migración aplicada es inmutable.
Nunca edites una migración ya aplicada
Si modificas el contenido de un archivo
V1__create_users_table.sql después de que fue ejecutado, Flyway va a detectar que el checksum cambió y va a lanzar un error al arrancar. La solución correcta es crear una nueva migración (V4, V5, etc.) con el cambio que necesitas. Las migraciones aplicadas son historia, no se reescriben.Migraciones repetibles
Además de las migraciones versionadas (V1, V2, V3...), Flyway soporta migraciones repetibles. Estas se ejecutan cada vez que su contenido cambia, y son útiles para objetos que se redefinen completos, como vistas o funciones:
R__create_user_summary_view.sql
CREATE OR REPLACE VIEW user_summary AS
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS total_orders,
COALESCE(SUM(o.total), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name, u.email;La convención es
R__<descripción>.sql (sin número de versión). Flyway las ejecuta después de todas las migraciones versionadas, y las vuelve a ejecutar si detecta que el archivo cambió.Buenas prácticas
- Un cambio por migración: no metas ALTER TABLE y CREATE TABLE en el mismo script. Si uno falla, quieres saber exactamente cuál.
- Nombres descriptivos:
V4__add_index_orders_status.sqles mejor queV4__fix.sql. - Siempre hacia adelante: no borres migraciones del historial. Si algo salió mal, crea una nueva migración que lo corrija.
- Usa transacciones: PostgreSQL ejecuta cada migración en una transacción por defecto. Si el script falla a la mitad, se hace rollback completo.
- Revisa en CI/CD: agrega
flyway validateen tu pipeline para detectar migraciones inconsistentes antes de llegar a producción. - Cuidado con los datos: las migraciones de esquema son seguras. Las migraciones de datos (INSERT, UPDATE, DELETE) requieren más cuidado: pruébalas en staging antes de producción.
Estructura final del proyecto
text
src/main/resources/
└── db/
└── migration/
├── V1__create_users_table.sql
├── V2__create_orders_table.sql
├── V3__add_phone_to_users.sql
└── R__create_user_summary_view.sqlEn resumen
Flyway convierte tus cambios de base de datos en código versionado. Cada migración es un archivo SQL con un número de versión, un checksum que garantiza integridad, y un registro en
flyway_schema_history que actúa como auditoría. Al integrarlo con Spring Boot, las migraciones se aplican automáticamente al arrancar la aplicación, manteniendo todos los ambientes sincronizados.La regla más importante: las migraciones aplicadas no se tocan. Siempre hacia adelante.