Start Learning
Javaneer
Back to stage
Module 3·Data & Persistence

Database Migrations with Flyway

Version your schema in checked-in SQL so every environment and teammate gets the same database, reproducibly.

14 min readIntermediate
On this page

Your entities describe the shape of your data, but who creates the actual tables? In development, Hibernate can auto-generate the schema - convenient, but dangerous: it drifts, it's not reviewable, and letting it alter a production database is a recipe for data loss. The professional answer is versioned migrations with Flyway.

Add Flyway

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

Boot auto-configures Flyway to run on startup, applying any pending migrations before the app serves traffic.

Migrations are ordered SQL files

Put versioned SQL scripts under src/main/resources/db/migration, named V<n>__<desc>.sql:

-- V1__create_book.sql
CREATE TABLE book (
    id     BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    isbn   VARCHAR(20) NOT NULL UNIQUE,
    title  VARCHAR(200) NOT NULL,
    author VARCHAR(120) NOT NULL,
    copies INT NOT NULL DEFAULT 0
);

Need to change the schema later? You don't edit V1 - you add a new migration:

-- V2__add_book_published_year.sql
ALTER TABLE book ADD COLUMN published_year INT;

Flyway records which versions have run in a flyway_schema_history table, so each migration applies exactly once, in order, on every database.

Migrations are git for your schema

You'd never email around a folder called "final_v3_REALLY_final" and hope everyone's code matches - you use version control, where history is ordered and immutable. Flyway is version control for your database: each migration is a committed change, history is append-only (you add, never rewrite), and any database can be brought to the current version by replaying the migrations it hasn't seen.

Never edit an applied migration

Once a migration has run anywhere, treat it as immutable - changing it makes its recorded checksum mismatch and Flyway will refuse to start. Always add a new migration to make further changes. In production, turn Hibernate's auto-DDL off (spring.jpa.hibernate.ddl-auto=validate) and let Flyway own the schema.

Ship a schema change safely

BookVault is live with a book table. You need to add a nullable published_year column. Explain why you add a new V2__…sql migration instead of editing the original V1, and what Flyway does on the next deploy to each environment.

Why version your schema with Flyway instead of letting Hibernate auto-generate it in production?

Key takeaways

  • Flyway versions your database schema as ordered, checked-in SQL migrations (V1__…, V2__…) applied automatically on startup.
  • Each migration runs exactly once per database, tracked in flyway_schema_history, so every environment converges to the same schema.
  • Change the schema by adding a NEW migration - never edit one that has already run (it breaks the checksum).
  • In production, set spring.jpa.hibernate.ddl-auto=validate and let Flyway own the schema, not Hibernate auto-DDL.
  • Migrations are version control for your database: ordered, immutable, reviewable history.
Was this lesson helpful?
Edit this page on GitHub