Liquibase Migrations
Liquibase is a database-agnostic schema migration tool that, like Flyway, version-controls your database. Its distinguishing feature is the changelog: a structured file (XML, YAML, JSON, or SQL) describing changes as portable changesets that Liquibase can translate to each database’s dialect. Spring Boot detects Liquibase on the classpath and applies pending changesets automatically on startup.
Why changelogs
Where Flyway leans on raw SQL scripts, Liquibase encourages declaring changes in a database-independent format. A createTable changeset generates the right AUTO_INCREMENT, BIGSERIAL, or IDENTITY syntax for whichever database you point it at, which is valuable when the same schema must run on MySQL in production and H2 in tests.
Adding the dependency
Add liquibase-core. Spring Boot manages the version.
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
The master changelog
Spring Boot looks for classpath:db/changelog/db.changelog-master.yaml by default. This master file does not usually contain changes itself; it includes the per-version changelogs in order.
src/main/resources/db/changelog/
├── db.changelog-master.yaml
├── 001-create-products.yaml
└── 002-add-category.yaml
# db.changelog-master.yaml
databaseChangeLog:
- include:
file: db/changelog/001-create-products.yaml
- include:
file: db/changelog/002-add-category.yaml
Changesets (YAML)
A changeset is the unit Liquibase applies and tracks. Each has an id and author; together with the file path they form a unique key. Once applied, a changeset must not change.
# 001-create-products.yaml
databaseChangeLog:
- changeSet:
id: 1
author: devcraftly
changes:
- createTable:
tableName: products
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints:
primaryKey: true
- column:
name: name
type: VARCHAR(120)
constraints:
nullable: false
- column:
name: price
type: DECIMAL(10,2)
Changesets (XML)
The same changeset in XML, which remains the most common Liquibase format in enterprise codebases:
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
<changeSet id="1" author="devcraftly">
<createTable tableName="products">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="name" type="VARCHAR(120)">
<constraints nullable="false"/>
</column>
<column name="price" type="DECIMAL(10,2)"/>
</createTable>
</changeSet>
</databaseChangeLog>
Configuration
Tell Spring Boot where the master changelog lives (only needed if you deviate from the default path), and let Liquibase own the schema.
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
jpa:
hibernate:
ddl-auto: validate
On startup Liquibase creates two bookkeeping tables, DATABASECHANGELOG (every applied changeset, by id/author/file) and DATABASECHANGELOGLOCK (a lock preventing concurrent migrations), then applies any pending changesets.
Warning: Never edit an applied changeset. Liquibase stores an MD5 checksum and fails at startup if it changes. To correct a mistake, add a new changeset.
Liquibase vs Flyway
Both are excellent and widely used. The choice usually comes down to whether you value database portability (Liquibase) or the simplicity of plain SQL (Flyway).
| Aspect | Liquibase | Flyway |
|---|---|---|
| Change format | XML, YAML, JSON, or SQL | SQL (Java for advanced) |
| Database portability | High (abstract changesets) | Lower (SQL is DB-specific) |
| Rollback support | Built-in (rollback tags) | Paid (U__ undo) |
| Learning curve | Steeper (changelog model) | Gentle (just SQL) |
| Tracking table | DATABASECHANGELOG | flyway_schema_history |
| Preconditions / contexts | Rich support | Limited |
| Best fit | Multi-database, complex rollbacks | SQL-first teams, simple flows |
Tip: Liquibase changesets support a
rollbackblock, so you can declare how to undo a change. This is a meaningful advantage over Flyway’s free tier, which has no built-in rollback.
Best Practices
- Keep one changeset per logical change and never modify an applied one.
- Use a master changelog that includes ordered per-version files for clean diffs.
- Set
ddl-auto: validateso Hibernate checks, but never alters, the schema. - Write
rollbackinstructions for changes that are not auto-reversible (raw SQL, data changes). - Run migrations against a throwaway database in CI before merging.