Liquibase in Spring Boot – Developer's Guide
Managing database schema changes can seem simple at first… until it isn’t. A quick ALTER TABLE here and there might work with one developer, but once you have multiple environments—dev, staging, prod—things get messy fast.
This is where Liquibase comes in.
In this guide, I’ll walk you through:
- What Liquibase is and why it matters
- Setting it up in Spring Boot
- Writing migrations safely
- Managing different environments
What is Liquibase?
Liquibase is essentially Git for your database schema. Instead of running SQL scripts manually and hoping everyone runs them correctly, you define changes in a structured format and Liquibase tracks which ones have been applied.
Example XML changeset:
<changeSet id="add-phone-number" author="dev">
<addColumn tableName="users">
<column name="phone_number" type="varchar(20)"/>
</addColumn>
</changeSet>
Liquibase keeps track of applied migrations in a table called DATABASECHANGELOG, ensuring:
- Changes are applied only once
- Order is maintained
- Rollbacks are possible
Why use Liquibase with Spring Boot?
Some key benefits:
- Consistent environments – dev, staging, and prod stay in sync
- Migration history – see who changed what and when
- Automatic migrations – Spring Boot can run them on startup
- Rollbacks – undo changes safely if needed
Adding Liquibase to your project
Maven:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
Gradle:
implementation 'org.liquibase:liquibase-core'
Spring Boot will automatically detect Liquibase and run migrations.
Configuration
In application.yml (safe YAML formatting):
spring:
liquibase:
change-log: "classpath:/db/changelog/changelog.xml"
contexts: "dev"
Typical project structure:
resources
└ db
└ changelog
└ changelog.xml
Master changelog with multiple changesets
Instead of separate XML files, you can put all changesets in a single file: changelog.xml
<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-3.8.xsd">
<changeSet id="create-users" author="you">
<createTable tableName="users">
<column name="id" type="bigint">
<constraints primaryKey="true"/>
</column>
<column name="email" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="created_at" type="timestamp"/>
</createTable>
</changeSet>
<changeSet id="add-email-index" author="you">
<createIndex indexName="idx_users_email" tableName="users">
<column name="email"/>
</createIndex>
</changeSet>
<changeSet id="add-phone-column" author="you">
<addColumn tableName="users">
<column name="phone_number" type="varchar(20)"/>
</addColumn>
<rollback>
<dropColumn tableName="users" columnName="phone_number"/>
</rollback>
</changeSet>
<changeSet id="insert-test-data" author="you" context="you">
<insert tableName="users">
<column name="id" valueNumeric="1"/>
<column name="email" value="test@example.com"/>
</insert>
</changeSet>
</databaseChangeLog>
Using SQL instead of XML
You can also write all changesets in a single formatted SQL file:
-- liquibase formatted sql
-- changeset dev:create-users
CREATE TABLE users (
id BIGINT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP
);
-- changeset dev:add-email-index
CREATE INDEX idx_users_email ON users(email);
-- changeset dev:add-phone-column
ALTER TABLE users ADD COLUMN phone_number VARCHAR(20);
-- rollback: ALTER TABLE users DROP COLUMN phone_number;
Best practices
- Never edit executed migrations – migrations are immutable
- One changeset = one change – separate tables, columns, and indexes
- Number your files if using multiple files – avoids Git conflicts
- Test locally – drop the DB, run the app, ensure migrations work from scratch
-
Be careful on production – avoid
ALTER COLUMN TYPE,DROP COLUMN, orNOT NULLon large tables
Liquibase vs Flyway
| Feature | Liquibase | Flyway |
|---|---|---|
| XML/YAML/JSON | ✔ | ✖ |
| SQL | ✔ | ✔ |
| Rollback | ✔ | limited |
Flyway is simpler but Liquibase is more flexible for enterprise setups.
Conclusion
Liquibase is a lifesaver for keeping database schema consistent across environments. Stick to the golden rules:
- immutable migrations
- one change per changeset
- test migrations locally
Thanks for reading!
Top comments (0)