Skip to content

Implement Flyway for database migrations #130

@nanotaboada

Description

@nanotaboada

Problem

The project currently lacks a structured approach to database schema versioning and migrations. This leads to:

  • Manual SQL script execution for schema changes
  • Inconsistent database states across environments (dev, test, prod)
  • Difficulty tracking which migrations have been applied
  • Risk of human error when deploying schema changes
  • No rollback strategy for failed migrations
  • Challenges onboarding new developers who need to set up local databases

Without a proper migration tool, database evolution becomes error-prone and difficult to manage as the application grows.

Required by: #286 (Add PostgreSQL support with unified migration-based initialization)

Proposed Solution

Integrate Flyway as the database migration tool for the Spring Boot project. This will:

  • Provide version control for database schemas
  • Enable automated, repeatable migrations across all environments
  • Track migration history in the database
  • Integrate seamlessly with Spring Boot auto-configuration
  • Allow easy rollback and repair of failed migrations
  • Ensure consistency between application code and database schema
  • Lay the groundwork for PostgreSQL support (Add PostgreSQL support with unified migration-based initialization #286) — migrations must use SQL compatible with both SQLite and PostgreSQL

Suggested Approach

1. Add Flyway dependencies

Update pom.xml:

<!-- Flyway Core -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

<!-- Flyway Database PostgreSQL (required for PostgreSQL support in Flyway 10+) -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-postgresql</artifactId>
</dependency>

2. Configure Flyway in Spring Boot

Update application.properties:

spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.jpa.hibernate.ddl-auto=validate

Remove spring.datasource.initialization-mode, schema.sql, and data.sql if present — Flyway takes over all schema management.

3. Create migration directory structure

src/main/resources/
└── db/
    └── migration/
        ├── V1__Create_players_table.sql
        ├── V2__Seed_starting11.sql
        └── V3__Seed_substitutes.sql

Educational note: Splitting seed data across two migrations illustrates the incremental, composable nature of the migration system. Rolling back V3 removes only the substitutes, leaving the starting 11 intact.

4. Migration naming convention

Follow Flyway's versioned migration pattern:

V{version}__{description}.sql

Examples:

  • V1__Create_players_table.sql
  • V2__Seed_starting11.sql
  • V3__Seed_substitutes.sql

5. Write cross-dialect compatible SQL

Migrations must work with both SQLite (local dev/test) and PostgreSQL (Docker/production, see #286). Avoid provider-specific syntax:

Avoid (SQLite-only) Avoid (PostgreSQL-only) Use instead
AUTOINCREMENT SERIAL / BIGSERIAL INTEGER PRIMARY KEY (SQLite auto-assigns; use identity columns in PG via Hibernate)
TEXT for all strings VARCHAR(n) enforced VARCHAR(n) works in both
INTEGER for booleans BOOLEAN INTEGER (Hibernate maps it correctly for both)

Hibernate/JPA with ddl-auto=validate will handle the ORM layer; Flyway handles the DDL. Keep migration SQL as standard as possible.

V1__Create_players_table.sql (example):

CREATE TABLE IF NOT EXISTS players (
    id          INTEGER PRIMARY KEY,
    firstName   VARCHAR(100),
    middleName  VARCHAR(100),
    lastName    VARCHAR(100),
    dateOfBirth VARCHAR(20),
    squadNumber INTEGER,
    position    VARCHAR(50),
    abbrPosition VARCHAR(10),
    team        VARCHAR(100),
    league      VARCHAR(100),
    starting11  INTEGER
);

V2__Seed_starting11.sql (example):

INSERT INTO players (firstName, middleName, lastName, dateOfBirth, squadNumber, position, abbrPosition, team, league, starting11)
VALUES
    ('Emiliano', 'Viviano', 'Martínez', '1992-08-02', 23, 'Goalkeeper', 'GK', 'Aston Villa', 'Premier League', 1),
    -- ... 10 more Starting XI players (starting11 = 1)
;

V3__Seed_substitutes.sql (example):

INSERT INTO players (firstName, middleName, lastName, dateOfBirth, squadNumber, position, abbrPosition, team, league, starting11)
VALUES
    ('Gerónimo', NULL, 'Rulli', '1992-05-07', 12, 'Goalkeeper', 'GK', 'Marseille', 'Ligue 1', 0),
    -- ... 13 more Substitute players (starting11 = 0)
;

6. Update database initialization logic

  • Remove any manual schema.sql / data.sql files
  • Remove spring.sql.init.* properties
  • Let Flyway handle all schema and seed data management

7. Update documentation

README.md:

  • Document migration workflow (how to create and apply new migrations)
  • Explain that the database auto-initializes via Flyway on startup
  • Add database reset workflow: delete the .db file and re-run the app

Acceptance Criteria

  • flyway-core and flyway-database-postgresql added to pom.xml
  • Flyway configured in application.properties (spring.flyway.enabled=true)
  • spring.jpa.hibernate.ddl-auto=validate (Flyway owns DDL, not Hibernate)
  • Migration directory created at src/main/resources/db/migration/
  • V1__Create_players_table.sql created with cross-dialect compatible SQL
  • V2__Seed_starting11.sql seeds the 11 Starting XI players (starting11 = 1)
  • V3__Seed_substitutes.sql seeds the 14 Substitute players (starting11 = 0)
  • Rolling back V3 removes only substitutes; V2 removes only Starting XI
  • Application starts successfully and Flyway applies migrations automatically
  • flyway_schema_history table is created and populated correctly
  • Manual schema.sql / data.sql files removed (if present)
  • All existing tests pass
  • Application can start from a clean database and build schema via migrations
  • Migration SQL is verified to be compatible with both SQLite and PostgreSQL
  • README.md updated with migration workflow

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestjavaPull requests that update Java codeplanningEnables automatic issue planning with CodeRabbitpriority highImportant for production readiness. Schedule for current milestone.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions