Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ Release names follow the **historic football clubs** naming convention (A–Z):

### Added

- Integrate Flyway for database schema versioning and automated migrations;
add `flyway-core` and `flyway-database-postgresql` to `pom.xml`; create
migration directory `src/main/resources/db/migration/` with three versioned
scripts: `V1__Create_players_table.sql` (schema), `V2__Seed_starting11.sql`
(11 Starting XI players), `V3__Seed_substitutes.sql` (15 substitute players);
configure `spring.flyway.enabled=true` with `baseline-on-migrate=true` for
backwards compatibility with existing databases; disable Flyway in test
environment which continues to use SQLite in-memory with `ddl.sql`/`dml.sql`;
switch `spring.jpa.hibernate.ddl-auto` from `none` to `validate` so Hibernate
verifies entity mappings against the Flyway-managed schema (#130)

### Changed

- Switch runtime base image from `eclipse-temurin:25-jdk-alpine` to
Expand Down
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,53 @@ spring.datasource.url=jdbc:sqlite::memory:
spring.jpa.hibernate.ddl-auto=create-drop
```

## Database Migrations

Schema versioning is managed by [Flyway](https://documentation.red-gate.com/flyway), which runs automatically on application startup and applies any pending migrations in order.

### Migration files

Versioned SQL scripts live in `src/main/resources/db/migration/` and follow the Flyway naming convention:

```
V{version}__{description}.sql
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

| File | Description |
| ---- | ----------- |
| `V1__Create_players_table.sql` | Creates the `players` table (schema) |
| `V2__Seed_starting11.sql` | Seeds 11 Starting XI players (`starting11 = 1`) |
| `V3__Seed_substitutes.sql` | Seeds 15 Substitute players (`starting11 = 0`) |

All migration SQL is written to be compatible with both **SQLite** (local dev) and **PostgreSQL** (see #286).

### Adding a new migration

Create a new file in `src/main/resources/db/migration/` with the next version number:

```bash
touch src/main/resources/db/migration/V4__Add_nationality_column.sql
```

Flyway applies it automatically on the next application startup. View the applied history by querying the `flyway_schema_history` table.

### Existing databases

`baseline-on-migrate=true` ensures that databases created before Flyway was introduced are recognised as already at `V3` (schema + full seed data), so no migrations run against them. Fresh databases (new file) run V1 → V2 → V3 from scratch.

### Reset local database

Delete the SQLite file and restart the application — Flyway recreates the schema and seed data automatically:

```bash
rm storage/players-sqlite3.db
./mvnw spring-boot:run
```

### Tests

The test environment keeps `spring.flyway.enabled=false` and uses SQLite in-memory with `ddl.sql`/`dml.sql` via Spring SQL init for fast, isolated test execution.

## Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on:
Expand Down
23 changes: 23 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,29 @@
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
</dependency>
<!-- Flyway Core ================================================== -->
<!--
Flyway is an open-source database migration tool. It provides version
control for your database schema and automates migrations across all
environments. Includes built-in support for SQLite (community).
https://mvnrepository.com/artifact/org.flywaydb/flyway-core
-->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- Flyway Database PostgreSQL ==================================== -->
<!--
Flyway 10+ modular architecture requires explicit database support
modules for non-community databases. Required for PostgreSQL support
(see #286 — Add PostgreSQL support with unified migration-based
initialization).
https://mvnrepository.com/artifact/org.flywaydb/flyway-database-postgresql
-->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<!-- H2 Database Engine ============================================ -->
<!--
Provides a fast in-memory database for testing. Used only in test
Expand Down
13 changes: 12 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,18 @@ springdoc.swagger-ui.path=/swagger/index.html
spring.datasource.url=jdbc:sqlite:${STORAGE_PATH:storage/players-sqlite3.db}
spring.datasource.driver-class-name=org.sqlite.JDBC
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=true

# Flyway Database Migration Configuration
# Flyway manages all schema creation and seed data via versioned SQL migrations.
# baseline-on-migrate: treats an existing database (without flyway_schema_history)
# as already at baseline-version, avoiding re-running migrations on pre-seeded DBs.
# baseline-version=3: baselines existing databases at V3 (schema + all seed data
# already applied), while fresh databases run V1 → V2 → V3 from scratch.
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.flyway.baseline-on-migrate=true
spring.flyway.baseline-version=3
21 changes: 21 additions & 0 deletions src/main/resources/db/migration/V1__Create_players_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- V1: Create players table
-- Compatible with both SQLite (local dev) and PostgreSQL (see #286).
-- TEXT columns use TEXT affinity in SQLite and the unlimited TEXT type in PostgreSQL.
-- INTEGER is used for squadNumber (natural key) and starting11 (boolean flag: 1/0).
-- UUID primary key is stored as VARCHAR(36) and generated at the application level.

CREATE TABLE IF NOT EXISTS players (
id VARCHAR(36) NOT NULL,
squadNumber INTEGER NOT NULL,
firstName TEXT NOT NULL,
middleName TEXT,
lastName TEXT NOT NULL,
dateOfBirth TEXT NOT NULL,
position TEXT NOT NULL,
abbrPosition TEXT NOT NULL,
team TEXT NOT NULL,
league TEXT NOT NULL,
starting11 INTEGER NOT NULL,
PRIMARY KEY (id),
UNIQUE (squadNumber)
);
16 changes: 16 additions & 0 deletions src/main/resources/db/migration/V2__Seed_starting11.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- V2: Seed Starting XI players (starting11 = 1)
-- Argentina 2022 FIFA World Cup squad — 11 players who started the final.
-- Rolling back this migration removes only the Starting XI, leaving substitutes intact.

INSERT INTO players (id, squadNumber, firstName, middleName, lastName, dateOfBirth, position, abbrPosition, team, league, starting11) VALUES
('01772c59-43f0-5d85-b913-c78e4e281452', 23, 'Damián', 'Emiliano', 'Martínez', '1992-09-02T00:00:00.000Z', 'Goalkeeper', 'GK', 'Aston Villa FC', 'Premier League', 1),

Check failure on line 6 in src/main/resources/db/migration/V2__Seed_starting11.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 4 times.

See more on https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&issues=AZ131zVofrTUmNjbgHJb&open=AZ131zVofrTUmNjbgHJb&pullRequest=310
('da31293b-4c7e-5e0f-a168-469ee29ecbc4', 26, 'Nahuel', NULL, 'Molina', '1998-04-06T00:00:00.000Z', 'Right-Back', 'RB', 'Atlético Madrid', 'La Liga', 1),
('c096c69e-762b-5281-9290-bb9c167a24a0', 13, 'Cristian','Gabriel', 'Romero', '1998-04-27T00:00:00.000Z', 'Centre-Back', 'CB', 'Tottenham Hotspur', 'Premier League', 1),
('d5f7dd7a-1dcb-5960-ba27-e34865b63358', 19, 'Nicolás', 'Hernán Gonzalo', 'Otamendi', '1988-02-12T00:00:00.000Z', 'Centre-Back', 'CB', 'SL Benfica', 'Liga Portugal', 1),

Check failure on line 9 in src/main/resources/db/migration/V2__Seed_starting11.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 3 times.

See more on https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&issues=AZ131zVofrTUmNjbgHJa&open=AZ131zVofrTUmNjbgHJa&pullRequest=310

Check failure on line 9 in src/main/resources/db/migration/V2__Seed_starting11.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 3 times.

See more on https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&issues=AZ131zVofrTUmNjbgHJZ&open=AZ131zVofrTUmNjbgHJZ&pullRequest=310
('2f6f90a0-9b9d-5023-96d2-a2aaf03143a6', 3, 'Nicolás', 'Alejandro', 'Tagliafico','1992-08-31T00:00:00.000Z', 'Left-Back', 'LB', 'Olympique Lyon', 'Ligue 1', 1),
('b5b46e79-929e-5ed2-949d-0d167109c022', 11, 'Ángel', 'Fabián', 'Di María', '1988-02-14T00:00:00.000Z', 'Right Winger', 'RW', 'SL Benfica', 'Liga Portugal', 1),
('0293b282-1da8-562e-998e-83849b417a42', 7, 'Rodrigo', 'Javier', 'de Paul', '1994-05-24T00:00:00.000Z', 'Central Midfield','CM', 'Atlético Madrid', 'La Liga', 1),

Check failure on line 12 in src/main/resources/db/migration/V2__Seed_starting11.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 3 times.

See more on https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&issues=AZ131zVofrTUmNjbgHJc&open=AZ131zVofrTUmNjbgHJc&pullRequest=310
('d3ba552a-dac3-588a-b961-1ea7224017fd', 24, 'Enzo', 'Jeremías', 'Fernández', '2001-01-17T00:00:00.000Z', 'Central Midfield','CM', 'SL Benfica', 'Liga Portugal', 1),
('9613cae9-16ab-5b54-937e-3135123b9e0d', 20, 'Alexis', NULL, 'Mac Allister','1998-12-24T00:00:00.000Z','Central Midfield','CM','Brighton & Hove Albion', 'Premier League', 1),
('acc433bf-d505-51fe-831e-45eb44c4d43c', 10, 'Lionel', 'Andrés', 'Messi', '1987-06-24T00:00:00.000Z', 'Right Winger', 'RW', 'Paris Saint-Germain', 'Ligue 1', 1),
('38bae91d-8519-55a2-b30a-b9fe38849bfb', 9, 'Julián', NULL, 'Álvarez', '2000-01-31T00:00:00.000Z', 'Centre-Forward', 'CF', 'Manchester City', 'Premier League', 1);
20 changes: 20 additions & 0 deletions src/main/resources/db/migration/V3__Seed_substitutes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- V3: Seed Substitute players (starting11 = 0)
-- Argentina 2022 FIFA World Cup squad — 15 players who did not start the final.
-- Rolling back this migration removes only the substitutes, leaving the Starting XI intact.

INSERT INTO players (id, squadNumber, firstName, middleName, lastName, dateOfBirth, position, abbrPosition, team, league, starting11) VALUES
('5a9cd988-95e6-54c1-bc34-9aa08acca8d0', 1, 'Franco', 'Daniel', 'Armani', '1986-10-16T00:00:00.000Z', 'Goalkeeper', 'GK', 'River Plate', 'Copa de la Liga', 0),
('5fdb10e8-38c0-5084-9a3f-b369a960b9c2', 2, 'Juan', 'Marcos', 'Foyth', '1998-01-12T00:00:00.000Z', 'Right-Back', 'RB', 'Villarreal', 'La Liga', 0),

Check failure on line 7 in src/main/resources/db/migration/V3__Seed_substitutes.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 5 times.

See more on https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&issues=AZ131zSJfrTUmNjbgHJY&open=AZ131zSJfrTUmNjbgHJY&pullRequest=310
('bbd441f7-fcfb-5834-8468-2a9004b64c8c', 4, 'Gonzalo', 'Ariel', 'Montiel', '1997-01-01T00:00:00.000Z', 'Right-Back', 'RB', 'Nottingham Forest', 'Premier League', 0),
('9d140400-196f-55d8-86e1-e0b96a375c83', 5, 'Leandro', 'Daniel', 'Paredes', '1994-06-29T00:00:00.000Z', 'Defensive Midfield','DM', 'AS Roma', 'Serie A', 0),

Check failure on line 9 in src/main/resources/db/migration/V3__Seed_substitutes.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal 4 times.

See more on https://sonarcloud.io/project/issues?id=nanotaboada_java.samples.spring.boot&issues=AZ131zSJfrTUmNjbgHJX&open=AZ131zSJfrTUmNjbgHJX&pullRequest=310
('d8bfea25-f189-5d5e-b3a5-ed89329b9f7c', 6, 'Germán', 'Alejo', 'Pezzella', '1991-06-27T00:00:00.000Z', 'Centre-Back', 'CB', 'Real Betis Balompié', 'La Liga', 0),
('dca343a8-12e5-53d6-89a8-916b120a5ee4', 8, 'Marcos', 'Javier', 'Acuña', '1991-10-28T00:00:00.000Z', 'Left-Back', 'LB', 'Sevilla FC', 'La Liga', 0),
('c62f2ac1-41e8-5d34-b073-2ba0913d0e31', 12, 'Gerónimo', NULL, 'Rulli', '1992-05-20T00:00:00.000Z', 'Goalkeeper', 'GK', 'Ajax Amsterdam', 'Eredivisie', 0),
('d3b0e8e8-2c34-531a-b608-b24fed0ef986', 14, 'Exequiel', 'Alejandro', 'Palacios', '1998-10-05T00:00:00.000Z', 'Central Midfield', 'CM', 'Bayer 04 Leverkusen', 'Bundesliga', 0),
('b1306b7b-a3a4-5f7c-90fd-dd5bdbed57ba', 15, 'Ángel', 'Martín', 'Correa', '1995-03-09T00:00:00.000Z', 'Right Winger', 'RW', 'Atlético Madrid', 'La Liga', 0),
('ecec27e8-487b-5622-b116-0855020477ed', 16, 'Thiago', 'Ezequiel', 'Almada', '2001-04-26T00:00:00.000Z', 'Attacking Midfield','AM', 'Atlanta United FC', 'Major League Soccer',0),
('7cc8d527-56a2-58bd-9528-2618fc139d30', 17, 'Alejandro', 'Darío', 'Gómez', '1988-02-15T00:00:00.000Z', 'Left Winger', 'LW', 'AC Monza', 'Serie A', 0),
('191c82af-0c51-526a-b903-c3600b61b506', 18, 'Guido', NULL, 'Rodríguez', '1994-04-12T00:00:00.000Z', 'Defensive Midfield','DM', 'Real Betis Balompié', 'La Liga', 0),
('7941cd7c-4df1-5952-97e8-1e7f5d08e8aa', 21, 'Paulo', 'Exequiel', 'Dybala', '1993-11-15T00:00:00.000Z', 'Second Striker', 'SS', 'AS Roma', 'Serie A', 0),
('79c96f29-c59f-5f98-96b8-3a5946246624', 22, 'Lautaro', 'Javier', 'Martínez', '1997-08-22T00:00:00.000Z', 'Centre-Forward', 'CF', 'Inter Milan', 'Serie A', 0),
('98306555-a466-5d18-804e-dc82175e697b', 25, 'Lisandro', NULL, 'Martínez', '1998-01-18T00:00:00.000Z', 'Centre-Back', 'CB', 'Manchester United', 'Premier League', 0);
4 changes: 4 additions & 0 deletions src/test/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ spring.sql.init.data-locations=classpath:dml.sql
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=true

# Flyway Configuration (disabled for tests)
# Tests use SQLite in-memory with Spring SQL init (ddl.sql + dml.sql) instead.
spring.flyway.enabled=false

# Server Configuration (disabled for tests)
server.port=0
management.server.port=0
Loading