Add db-migrations-flyway-liquibase: Flyway vs Liquibase migrations, rollbacks and baselines on Spring Boot 4.1

Companion code for the Flyway vs Liquibase article: checksum validation, out-of-order
and repeatable migrations, baselining an existing schema, Flyway Community's undo/diff/deploy
stubs, concurrent-startup locking for both tools, Liquibase changeset identity and rollback
(auto-generated vs explicit), a verified Liquibase 5.0.3 filename-caching defect, the new
OSS license service, the FSL license change, and running both tools against one database.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q6XdRjtsp4862EM44T7i9a
This commit is contained in:
2026-09-15 07:08:57 +00:00
co-authored by Claude Sonnet 5
parent b02fbe1416
commit 3908331431
61 changed files with 3128 additions and 0 deletions
@@ -0,0 +1,20 @@
========================================================
Flyway: two versioned migrations, applied on startup
========================================================
captured: 2026-09-15T07:07:26.946024742Z
-- flyway_schema_history --
installed_rank | version | description | type | checksum | success
---------------+---------+-------------------------------------------+-------+-------------+--------
-1 | NULL | << Flyway Schema History table created >> | TABLE | NULL | true
1 | 1 | create customer | SQL | 1461549807 | true
2 | 2 | seed customer | SQL | -1214875726 | true
(3 rows)
-- customer table --
ID | NAME | EMAIL
---+--------------+------------------
1 | Ada Lovelace | [email protected]
2 | Grace Hopper | [email protected]
(2 rows)
@@ -0,0 +1,16 @@
=====================================================
Flyway: editing an already-applied migration file
=====================================================
captured: 2026-09-15T07:07:27.074690371Z
-- first startup — V1 applied as originally written --
context started cleanly, V1__init.sql applied
-- second startup — V1__init.sql edited after being applied --
FlywayValidateException: Validate failed: Migrations have failed validation
Migration checksum mismatch for migration version 1
-> Applied to database : -712784830
-> Resolved locally : 756220147
Either revert the changes to the migration, or run repair to update the schema history.
Need more flexibility with validation rules? Learn more: https://help.red-gate.com/help/flyway-cli12/help_4.aspx?topic=flyway-blog/older-posts/customize-validation-rules-with-ignoremigrationpatterns
@@ -0,0 +1,29 @@
==============================================================================
Flyway: a lower-versioned migration lands after a higher one is already applied
==============================================================================
captured: 2026-09-15T07:07:28.552801366Z
-- first startup — V1 and V3 only --
applied: V1, V3
-- second startup — outOfOrder=false (Flyway default) --
context failed to start: FlywayValidateException: Validate failed: Migrations have failed validation
Detected resolved migration not applied to database: 2.
To ignore this migration, set -ignoreMigrationPatterns='*:ignored'. To allow executing this migration, set -outOfOrder=true.
Need more flexibility with validation rules? Learn more: https://help.red-gate.com/help/flyway-cli12/help_4.aspx?topic=flyway-blog/older-posts/customize-validation-rules-with-ignoremigrationpatterns
installed_rank | version | description | success
---------------+---------+-------------------------------------------+--------
-1 | NULL | << Flyway Schema History table created >> | true
1 | 1 | init | true
2 | 3 | add note index | true
(3 rows)
-- third startup — outOfOrder=true --
installed_rank | version | description | success
---------------+---------+-------------------------------------------+--------
-1 | NULL | << Flyway Schema History table created >> | true
1 | 1 | init | true
2 | 3 | add note index | true
3 | 2 | add note length check | true
(4 rows)
@@ -0,0 +1,36 @@
==================================================================
Flyway: a repeatable migration reruns on checksum change alone
==================================================================
captured: 2026-09-15T07:07:28.316653172Z
-- first startup --
installed_rank | version | description | type | checksum
---------------+---------+-------------------------------------------+-------+------------
-1 | NULL | << Flyway Schema History table created >> | TABLE | NULL
1 | 1 | init | SQL | -192856793
2 | NULL | invoice summary view | SQL | -2025099931
(3 rows)
-- invoice_summary right after creation --
STATUS | CNT
--------+----
PAID | 1
PENDING | 1
(2 rows)
-- second startup — only R__invoice_summary_view.sql changed --
installed_rank | version | description | type | checksum
---------------+---------+-------------------------------------------+-------+------------
-1 | NULL | << Flyway Schema History table created >> | TABLE | NULL
1 | 1 | init | SQL | -192856793
2 | NULL | invoice summary view | SQL | -2025099931
3 | NULL | invoice summary view | SQL | 750256380
(4 rows)
-- invoice_summary after the repeatable migration reran --
STATUS | CNT | TOTAL_CENTS
--------+-----+------------
PAID | 1 | 5000
PENDING | 1 | 3000
(2 rows)
@@ -0,0 +1,22 @@
==================================================
Flyway: adopting an existing, unmanaged schema
==================================================
captured: 2026-09-15T07:07:29.126413637Z
-- migrate() with no baseline configuration, against a non-empty schema --
FlywayException: Found non-empty schema(s) "PUBLIC" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.
-- migrate() with baselineOnMigrate=true, baselineVersion=1 --
installed_rank | version | description | type | success
---------------+---------+-------------------------------------------+----------+--------
-1 | NULL | << Flyway Schema History table created >> | TABLE | true
1 | 1 | pre-flyway schema | BASELINE | true
2 | 2 | add status column | SQL | true
(3 rows)
-- legacy_account keeps its pre-existing row and gains the new column --
ID | OWNER | STATUS
---+-----------------+-------
1 | pre-flyway-data | ACTIVE
(1 row)
@@ -0,0 +1,8 @@
==============================================================
Flyway: calling the public undo() API on the Community jar
==============================================================
captured: 2026-09-15T07:07:29.409916017Z
flyway.undo() threw: org.flywaydb.core.internal.license.FlywayRedgateEditionRequiredException
message: Flyway Redgate Edition Required: undo is not supported by OSS Edition
Download Redgate Edition for free: https://rd.gt/3GGIXhh
@@ -0,0 +1,17 @@
==============================================================================
Flyway: every command name that resolves to a Redgate-edition-required stub in flyway-core
==============================================================================
captured: 2026-09-15T07:07:26.936295299Z
- auth
- check
- deploy
- diff
- difftext
- generate
- licensingconfigurationextensionstub.class
- model
- offlinepermitconfigurationextensionstub.class
- pattokenconfigurationextensionstub.class
- prepare
- undo
@@ -0,0 +1,15 @@
==============================================================
Flyway: two instances calling migrate() at the same moment
==============================================================
captured: 2026-09-15T07:07:28.249758300Z
instance A migrate() took 932ms
instance B migrate() took 910ms
wall-clock time for both, run concurrently: 937ms
-- flyway_schema_history after both finished --
installed_rank | version | description | success
---------------+---------+-------------------------------------------+--------
-1 | NULL | << Flyway Schema History table created >> | true
1 | 1 | SlowMigration | true
(2 rows)
@@ -0,0 +1,18 @@
=================================================
Liquibase: two changesets, applied on startup
=================================================
captured: 2026-09-15T07:07:15.896321149Z
-- databasechangelog --
ID | AUTHOR | FILENAME | ORDEREXECUTED | EXECTYPE
-----------------+--------+-------------+---------------+---------
1-create-account | ankurm | master.yaml | 1 | EXECUTED
2-seed-account | ankurm | master.yaml | 2 | EXECUTED
(2 rows)
-- account table --
ID | OWNER
---+------------------
1 | Katherine Johnson
(1 row)
@@ -0,0 +1,12 @@
======================================================
Liquibase: auto-generated rollback for createTable
======================================================
captured: 2026-09-15T07:07:16.057288822Z
after update(): session table exists = true
after rollback(1): session table exists = false
-- databasechangelog after rollback --
ID | EXECTYPE
---+---------
(0 rows)
@@ -0,0 +1,8 @@
========================================================================
Liquibase: rolling back an insert changeset with no <rollback> block
========================================================================
captured: 2026-09-15T07:07:26.800580455Z
after update(): both changesets applied
rollback(1) threw: liquibase.exception.CommandExecutionException
message: liquibase.exception.LiquibaseException: liquibase.exception.RollbackFailedException: liquibase.exception.RollbackImpossibleException: No inverse to liquibase.change.core.InsertDataChange created
@@ -0,0 +1,16 @@
===================================================================
Liquibase: the same insert, now with an explicit rollback block
===================================================================
captured: 2026-09-15T07:07:16.370040970Z
-- audit_log right after update() --
ID | EVENT
---+-------------
1 | system-start
(1 row)
-- audit_log after rollback(1) — table still exists, the row is gone --
ID | EVENT
---+------
(0 rows)
@@ -0,0 +1,20 @@
================================================================
Liquibase: two instances calling update() at the same moment
================================================================
captured: 2026-09-15T07:07:26.705434740Z
instance A update() took 874ms
instance B update() took 10097ms
wall-clock time for both, run concurrently: 10099ms
-- databasechangeloglock after both finished --
ID | LOCKED | LOCKEDBY
---+--------+---------
1 | false | NULL
(1 row)
-- databasechangelog after both finished --
ID | AUTHOR | EXECTYPE
--------------+--------+---------
1-slow-change | ankurm | EXECUTED
(1 row)
@@ -0,0 +1,9 @@
==============================================================================
Liquibase: the LicenseService actually wired up on the classpath Boot uses
==============================================================================
captured: 2026-09-15T07:07:16.051670223Z
implementation: liquibase.license.OSSLicenseService
licenseIsInstalled(): false
licenseIsValid("any"): false
getLicenseInfo(): ""
@@ -0,0 +1,34 @@
=======================================================================
Flyway and Liquibase, both enabled: the naive failure, then the fix
=======================================================================
captured: 2026-09-15T07:07:11.366831858Z
-- naive: both enabled, no other configuration --
context failed to start: FlywayException: Found non-empty schema(s) "PUBLIC" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.
-- fixed: spring.flyway.baseline-on-migrate=true, spring.flyway.baseline-version=0 --
TABLE_NAME
---------------------
CUSTOMER
DATABASECHANGELOG
DATABASECHANGELOGLOCK
PRODUCT
flyway_schema_history
(5 rows)
-- flyway_schema_history — baseline row at 0, then V1/V2 ran for real --
version | description | type | success
--------+-------------------------------------------+----------+--------
NULL | << Flyway Schema History table created >> | TABLE | true
0 | << Flyway Baseline >> | BASELINE | true
1 | create customer | SQL | true
2 | seed customer | SQL | true
(4 rows)
-- databasechangelog — Liquibase's own bookkeeping, untouched by Flyway --
ID | AUTHOR | EXECTYPE
-----------------+--------+---------
1-create-product | ankurm | EXECUTED
2-seed-product | ankurm | EXECUTED
(2 rows)