Databases 19 min read

Mastering Liquibase with Spring Boot: A Step‑by‑Step Database Change Management Guide

This tutorial walks through setting up Liquibase in a Spring Boot project, creating a Maven plugin to generate XML/YAML changelogs, configuring the application, and performing common database operations such as creating tables, adding columns, creating indexes, initializing data, while also addressing a common includeAll path issue.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Liquibase with Spring Boot: A Step‑by‑Step Database Change Management Guide

This article introduces Liquibase, a database change management tool, and demonstrates a practical Spring Boot project that uses Liquibase to create tables, modify columns, add indexes, and initialize data.

Liquibase Template Generator Plugin

The tutorial first creates a Maven project liquibase-changelog-generate that can generate changelog files in both XML and YAML formats. The plugin declares the following dependencies:

<dependencies>
  <dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-plugin-api</artifactId>
    <version>3.8.6</version>
  </dependency>
  <dependency>
    <groupId>org.apache.maven.plugin-tools</groupId>
    <artifactId>maven-plugin-annotations</artifactId>
    <version>3.6.4</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.5</version>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-plugin-plugin</artifactId>
      <version>3.6.4</version>
      <configuration>
        <goalPrefix>hresh</goalPrefix>
        <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>2.6.3</version>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

An interface LiquibaseChangeLog provides utility methods for generating changelog file names and checking duplicate IDs:

public interface LiquibaseChangeLog {
  default String getChangeLogFileName(String sourceFolderPath) {
    System.out.println("> Please enter the id of this change:");
    Scanner scanner = new Scanner(System.in);
    String changeId = scanner.nextLine();
    if (StrUtil.isBlank(changeId)) {
      return null;
    }
    String changeIdPattern = "^[a-z][a-z0-9_]*$";
    Pattern pattern = Pattern.compile(changeIdPattern);
    Matcher matcher = pattern.matcher(changeId);
    if (!matcher.find()) {
      System.out.println("Change id should match " + changeIdPattern);
      return null;
    }
    if (isExistedChangeId(changeId, sourceFolderPath)) {
      System.out.println("Duplicate change id :" + changeId);
      return null;
    }
    Date now = new Date();
    String timestamp = DateUtil.format(now, "yyyyMMdd_HHmmss_SSS");
    return timestamp + "__" + changeId;
  }

  default boolean isExistedChangeId(String changeId, String sourceFolderPath) {
    File file = new File(sourceFolderPath);
    File[] files = file.listFiles();
    if (files == null) {
      return false;
    }
    for (File f : files) {
      if (f.isFile() && f.getName().contains(changeId)) {
        return true;
      }
    }
    return false;
  }
}

Two Mojo classes implement the interface: LiquibaseChangeLogXml generates an XML changelog, and LiquibaseChangeLogYaml generates a YAML changelog. Both obtain the Git user name via a GitUtil helper:

public class GitUtil {
  public static String getGitUserName() {
    try {
      String cmd = "git config user.name";
      Process p = Runtime.getRuntime().exec(cmd);
      BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
      String line = reader.readLine();
      p.waitFor();
      reader.close();
      p.destroy();
      return line;
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
    return "hresh";
  }
}

Spring Boot Liquibase Project

The second project, springboot-liquibase , demonstrates how Liquibase integrates with Spring Boot. Key Maven dependencies include Spring Boot starter web, MySQL connector, Druid datasource, MyBatis‑Plus, and Liquibase core:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.version}</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>${druid.version}</version>
  </dependency>
  <dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.16.1</version>
  </dependency>
  <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
  </dependency>
</dependencies>

The application.yml configures the datasource and Liquibase settings:

server:
  port: 8088

spring:
  application:
    name: springboot-liquibase
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mysql_db?serverTimezone=Hongkong&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
  liquibase:
    enabled: true
    change-log: classpath:liquibase/master.xml
    database-change-log-table: databasechangelog
    database-change-log-lock-table: databasechangeloglock

mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    lazy-loading-enabled: true

changeLogFile: src/main/resources/liquibase/master.xml

The master changelog ( master.xml) includes common properties and an includeAll directive that points to liquibase/changelogs/:

<?xml version="1.0" encoding="UTF-8"?>
<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">
  <property name="id" value="int(11)" dbms="mysql"/>
  <property name="time" value="timestamp" dbms="mysql"/>
  <includeAll path="liquibase/changelog/"/>
</databaseChangeLog>

Database Operations

Creating a table – after running mvn install, the plugin generates a YAML changelog named 20221124_161016_997__create_table_admin.yml with the following content:

databaseChangeLog:
  - changeSet:
      id: 20221124_161016_997__create_table_admin
      author: hresh
      changes:
        - createTable:
            tableName: admin
            columns:
              - column:
                  name: id
                  type: ${id}
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: name
                  type: varchar(50)
              - column:
                  name: password
                  type: varchar(100)
              - column:
                  name: create_time
                  type: ${time}

Adding a column – the plugin creates 20221124_163754_923__add_column_address_in_admin.yml:

databaseChangeLog:
  - changeSet:
      id: 20221124_163754_923__add_column_address_in_admin
      author: hresh
      changes:
        - addColumn:
            tableName: admin
            columns:
              - column:
                  name: address
                  type: varchar(100)

Creating an index – example changelog:

databaseChangeLog:
  - changeSet:
      id: 20221124_164641_992__create_index_in_admin
      author: hresh
      changes:
        - createIndex:
            tableName: admin
            indexName: idx_name
            columns:
              - column:
                  name: name

To modify an index, first drop it using a dropIndex changeSet.

Initializing data – a simple SQL changeSet inserts a record into the admin table:

databaseChangeLog:
  - changeSet:
      id: 20221124_165413_348__init_data_in_admin
      author: hresh
      changes:
        - sql:
            dbms: mysql
            sql: "insert into admin(name,password) values('hresh','1234')"
            stripComments: true

Common Issue: Unwanted Files Loaded by includeAll

The original includeAll path classpath:/liquibase/changelog/** caused Liquibase to scan the same path inside the liquibase‑core JAR, resulting in many irrelevant files being listed in the console. Renaming the project directory from changelog to changelogs and updating the includeAll path resolves the problem.

After the rename, the console output shows only the intended changelog files.

Liquibase console command
Liquibase console command

Running the application displays the generated databasechangelog table entries and the newly created admin table structure.

databasechangelog table
databasechangelog table
admin table fields
admin table fields

Further Liquibase commands such as liquibase:dbDoc can generate visual documentation of the database schema, which is output to the target directory and can be viewed via index.html.

Liquibase generated documentation
Liquibase generated documentation
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

SQLSpring BootYAMLdatabase migrationchangelogMaven PluginLiquibase
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.