Schema migrations with Flyway and seed data
Versioned and repeatable migrations, baselining an existing schema, repeatable seed scripts, and a rollback strategy that works.
Versioned migrations
Set spring.jpa.hibernate.ddl-auto to validate and let Flyway own the schema. Hibernate then fails fast when the entity no longer matches the table instead of quietly altering it.
-- src/main/resources/db/migration/V1__create_book_table.sql
create table book (
id bigserial primary key,
title text not null,
author_id bigint not null references author(id),
published_at timestamptz,
created_at timestamptz not null default now()
);
create index idx_book_author on book(author_id);
-- V2__add_isbn_to_book.sql
alter table book add column isbn text;
create unique index idx_book_isbn on book(isbn) where isbn is not null;| Prefix | Runs | Use for |
|---|---|---|
V1__, V2__ | Exactly once, in order | Schema changes |
R__ | Whenever the checksum changes, after versioned ones | Views, functions, seed reference data |
U1__ | Manually triggered | Undo scripts, when you insist on rollback files |
Configuration and baselining
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
baseline-version: 0
out-of-order: false
validate-on-migrate: true
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
jdbc:
time_zone: UTCbaseline-on-migrate is for bringing an existing database under Flyway control: it writes a baseline row so migrations above that version apply and ones below are skipped. Use it once, with care, never as a daily setting.
- One migration per logical change, small enough to review in a diff.
- Adding a NOT NULL column needs three steps: add nullable, backfill, then add the constraint.
- Run migrations at deploy time as a separate step so a failure stops the release before the new code starts.
Reference data and test seeding
-- R__seed_countries.sql (repeatable: re-runs when the file changes)
insert into country (code, name)
values ('DE', 'Germany'), ('FR', 'France'), ('GB', 'United Kingdom')
on conflict (code) do update set name = excluded.name;Keep deterministic reference data in repeatable migrations. For per-environment demo rows, use a Spring CommandLineRunner guarded by a profile, or Testcontainers with a @Sql script - do not put demo users in a versioned migration that reaches production.
FAQ
How do I roll back a bad migration?
Flyway or Liquibase?
Related
Configuration, JPA data access and profiles Testing Spring Boot applications
Last refreshed 2026-09-18.