DEV Community

Manikanta Yarramsetti
Manikanta Yarramsetti

Posted on

Liquibase in Spring Boot

What is Liquibase?

Liquibase is a database schema change management tool that tracks, versions, and deploys database changes. It supports multiple databases and file formats including XML, YAML, JSON, and SQL.

Quick Setup

Add dependency to pom.xml:

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Configure in application.properties:

spring.liquibase.enabled=true
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml
spring.liquibase.contexts=dev,prod
Enter fullscreen mode Exit fullscreen mode

Project Structure

src/main/resources/
  db/
    changelog/
      db.changelog-master.yaml
      changes/
        001-create-users.yaml
        002-add-email-column.yaml
Enter fullscreen mode Exit fullscreen mode

Master Changelog

db.changelog-master.yaml:

databaseChangeLog:
  - include:
      file: db/changelog/changes/001-create-users.yaml
  - include:
      file: db/changelog/changes/002-add-email-column.yaml
Enter fullscreen mode Exit fullscreen mode

Creating Tables

001-create-users.yaml:

databaseChangeLog:
  - changeSet:
      id: 001
      author: developer
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: BIGINT
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: username
                  type: VARCHAR(50)
                  constraints:
                    nullable: false
                    unique: true
              - column:
                  name: created_at
                  type: TIMESTAMP
                  defaultValueComputed: CURRENT_TIMESTAMP
Enter fullscreen mode Exit fullscreen mode

Adding Columns

002-add-email-column.yaml:

databaseChangeLog:
  - changeSet:
      id: 002
      author: developer
      changes:
        - addColumn:
            tableName: users
            columns:
              - column:
                  name: email
                  type: VARCHAR(100)
                  constraints:
                    nullable: false
                    unique: true
Enter fullscreen mode Exit fullscreen mode

Creating Foreign Keys

003-create-orders.yaml:

databaseChangeLog:
  - changeSet:
      id: 003
      author: developer
      changes:
        - createTable:
            tableName: orders
            columns:
              - column:
                  name: id
                  type: BIGINT
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: user_id
                  type: BIGINT
              - column:
                  name: total
                  type: DECIMAL(10,2)
        - addForeignKeyConstraint:
            baseTableName: orders
            baseColumnNames: user_id
            referencedTableName: users
            referencedColumnNames: id
            constraintName: fk_orders_user
Enter fullscreen mode Exit fullscreen mode

Rollback Support

Define rollback for each change:

databaseChangeLog:
  - changeSet:
      id: 004
      author: developer
      changes:
        - addColumn:
            tableName: users
            columns:
              - column:
                  name: phone
                  type: VARCHAR(20)
      rollback:
        - dropColumn:
            tableName: users
            columnName: phone
Enter fullscreen mode Exit fullscreen mode

Using Contexts

Different changes for different environments:

databaseChangeLog:
  - changeSet:
      id: 005
      author: developer
      context: dev
      changes:
        - insert:
            tableName: users
            columns:
              - column:
                  name: username
                  value: test_user
              - column:
                  name: email
                  value: test@example.com
Enter fullscreen mode Exit fullscreen mode

Preconditions

Add safety checks before execution:

databaseChangeLog:
  - changeSet:
      id: 006
      author: developer
      preConditions:
        - not:
            - columnExists:
                tableName: users
                columnName: status
      changes:
        - addColumn:
            tableName: users
            columns:
              - column:
                  name: status
                  type: VARCHAR(20)
Enter fullscreen mode Exit fullscreen mode

Using SQL Files

For complex migrations:

databaseChangeLog:
  - changeSet:
      id: 007
      author: developer
      changes:
        - sqlFile:
            path: db/changelog/sql/007-migration.sql
Enter fullscreen mode Exit fullscreen mode

007-migration.sql:

-- liquibase formatted sql

-- changeset developer:007
UPDATE users SET status = 'ACTIVE' WHERE status IS NULL;
CREATE INDEX idx_users_status ON users(status);

-- rollback DROP INDEX idx_users_status;
-- rollback UPDATE users SET status = NULL WHERE status = 'ACTIVE';
Enter fullscreen mode Exit fullscreen mode

Data Migration

Update existing data:

databaseChangeLog:
  - changeSet:
      id: 008
      author: developer
      changes:
        - update:
            tableName: users
            columns:
              - column:
                  name: status
                  value: LEGACY
            where: created_at < '2020-01-01'
Enter fullscreen mode Exit fullscreen mode

Load Data from CSV

databaseChangeLog:
  - changeSet:
      id: 009
      author: developer
      changes:
        - loadData:
            tableName: categories
            file: db/data/categories.csv
            columns:
              - column:
                  name: name
                  type: STRING
              - column:
                  name: description
                  type: STRING
Enter fullscreen mode Exit fullscreen mode

Creating Indexes

databaseChangeLog:
  - changeSet:
      id: 010
      author: developer
      changes:
        - createIndex:
            indexName: idx_username
            tableName: users
            columns:
              - column:
                  name: username
Enter fullscreen mode Exit fullscreen mode

Common Change Types

Change Type Purpose
createTable Create new table
dropTable Remove table
addColumn Add column
dropColumn Remove column
modifyDataType Change column type
renameColumn Rename column
createIndex Add index
addForeignKeyConstraint Add foreign key
insert Insert data
update Update data
sql Execute custom SQL

Programmatic Configuration

@Configuration
public class LiquibaseConfig {

    @Bean
    public SpringLiquibase liquibase(DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:db/changelog/db.changelog-master.yaml");
        liquibase.setContexts("dev,prod");
        return liquibase;
    }
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

Never modify executed changesets. Use meaningful changeset IDs like 001, 002 or dates. Include author in every changeset. Use contexts for environment-specific changes. Always define rollback for production. Test migrations in development first. Keep changesets small and focused. Add preconditions to prevent errors. Document complex migrations with comments.

Troubleshooting

Clear checksums after manual fixes:

-- View changelog table
SELECT * FROM databasechangelog;

-- Clear checksums
UPDATE databasechangelog SET md5sum = NULL;
Enter fullscreen mode Exit fullscreen mode

Mark changeset as executed manually:

INSERT INTO databasechangelog (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, EXECTYPE)
VALUES ('011', 'developer', 'changelog.yaml', NOW(), 11, 'EXECUTED');
Enter fullscreen mode Exit fullscreen mode

Liquibase vs Flyway

Feature Liquibase Flyway
File Formats XML, YAML, JSON, SQL SQL primarily
Rollback Built-in Paid feature
Database Agnostic Excellent Good
Learning Curve Moderate Easy
Preconditions Yes No

When to Use Liquibase

Choose Liquibase when you need database portability across MySQL, PostgreSQL, Oracle. Need built-in rollback capabilities. Want to use YAML or XML instead of SQL. Require conditional migrations with contexts. Need preconditions for safety checks. Working on complex enterprise applications.

Top comments (0)