Spring Boot Liquibase Tutorial

Updated:

Tutorial: use Liquibase with Spring to update your database

In this example we look at the integration between liquibase and Spring Boot. We will create a basic example with scripts that will run in a memory H2 database for the local environment and in PostgreSQL for the 'production'.

What you can find in the post:

  • integration of liquibase with Spring Boot
  • deployment of the same scripts for H2 in memory database for local development and PostgreSQL for production
  • configuration using XML and SQL to avoid SnakeYml issues
  • call of multiple liquibase databaseChangeLog in hierarchy

To reduce the depencies to external libraries (and issues) we won't use Liquibase Client and we will configure the application and liquibase using simple SQL and XML.
We could not se yaml or json because of a security issue in SnakeYaml (1.3) that is used by Liquibase to parse yaml and JSON. The issue is fixed with SnakeYML 2.0 but this library was not compatible with Spring Boot 3.0.x yet.

  1. Structure of the project
  2. Database scripts
  3. Liquibase configuration
  4. Spring Boot configuration

The complete code of the application is available here:
https://github.com/marco76/LiquibaseDemo

Liquibase files

structure of the files

In our structure we have 2 main changelogs. db.changelog-h2.xml is used for the local dev environment (memory database). Every time we start the application we have the same fresh data in memory.

For the instances of integration and production we use a PostgreSQL database. These environments use the db.changelog-postgresql.xmlfile.

The script for PostgreSQL and H2 are the same, the small differences are integrated in h2-function.sql that convert some PostgreSQL features in H2.

Liquibase H2 Changelog

<include file="/db.changelog/changes/h2-functions.sql"/> 
<include file="/db.changelog/db.changelog-postgresql.xml" /> 
<includeAll path="/db.changelog/changes/h2-data/"/> 

The H2 Changelog:

  • import some functions in h2 that are not available by default
  • executes the 'official' changelog, the one for PostgreSQL
  • load the data in the database for the local/dev environment

Liquibase PostgreSQL Changelog

<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.17.xsd"> 
    <include file="/db.changelog/changes/schema.sql"/> 
</databaseChangeLog> 

The PGSQL changelog load the schema files that will contains all the changes to apply in the DB. We opted to have only 1 file with multiples changesets but you could split the changes in multiples files.

The content of the schema.sql:

-- liquibase formatted sql 
 
-- changeset marco:1 
CREATE TABLE PERSON 
( 
    ID  UUID not null default gen_random_uuid(), 
    NAME VARCHAR not null 
); 

SpringBoot and Liquibase

To load The Liquibase data in our H2 Database we need to add some dependecies in our pom.xml:

<dependency> 
  <groupId>org.liquibase</groupId> 
  <artifactId>liquibase-core</artifactId> 
</dependency> 
 
<dependency> 
  <groupId>com.h2database</groupId> 
  <artifactId>h2</artifactId> 
  <scope>runtime</scope> 
</dependency> 

You should add the dependency of your production DB too (in our case PostgreSQL).

Spring Boot recognize H2 and Liquibase and try to autoconfigure them automatically.

H2 DB configuration

spring.datasource.url=jdbc:h2:mem:db;MODE=PostgreSQL;Database_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=TRUE; 
spring.datasource.username=sa 
spring.datasource.password=sa 
spring.datasource.driver-class-name=org.h2.Driver 
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 
spring.jpa.properties.hibernate.show_sql=true 
spring.jpa.properties.hibernate.use_sql_comments=true 
spring.jpa.properties.hibernate.format_sql=true 
spring.h2.console.enabled=true 
spring.h2.console.path=/console 
spring.sql.init.platform=h2 
spring.jpa.hibernate.ddl-auto=none 
spring.liquibase.change-log=classpath:/db.changelog/db.changelog-h2.xml 

In the configuration of our H2 database we add the reference to the spring.liquibase.change-log file. This triggers the scripts when the application starts.

Start the application

When you start the application you should see in the log that the database has been correctly loaded/updated:

2023-03-27T22:42:25.510+02:00  INFO 68851 --- [           main] o.s.b.a.h2.H2ConsoleAutoConfiguration    : H2 console available at '/console'. Database available at 'jdbc:h2:mem:db' 
2023-03-27T22:42:25.731+02:00  INFO 68851 --- [           main] liquibase.database                       : Set default schema name to public 
2023-03-27T22:42:25.889+02:00  INFO 68851 --- [           main] liquibase.lockservice                    : Successfully acquired change log lock 
2023-03-27T22:42:26.273+02:00  INFO 68851 --- [           main] liquibase.changelog                      : Reading resource: db.changelog/changes/h2-data/001-dev-data.sql 
2023-03-27T22:42:26.298+02:00  INFO 68851 --- [           main] liquibase.changelog                      : Creating database history table with name: public.DATABASECHANGELOG 
2023-03-27T22:42:26.305+02:00  INFO 68851 --- [           main] liquibase.changelog                      : Reading from public.DATABASECHANGELOG 
Running Changeset: db.changelog/changes/h2-functions.sql::1::marco 
2023-03-27T22:42:27.147+02:00  INFO 68851 --- [           main] liquibase.changelog                      : Custom SQL executed 
2023-03-27T22:42:27.148+02:00  INFO 68851 --- [           main] liquibase.changelog                      : ChangeSet db.changelog/changes/h2-functions.sql::1::marco ran successfully in 777ms 
Running Changeset: db.changelog/changes/schema.sql::1::marco 
2023-03-27T22:42:27.202+02:00  INFO 68851 --- [           main] liquibase.changelog                      : Custom SQL executed 
2023-03-27T22:42:27.203+02:00  INFO 68851 --- [           main] liquibase.changelog                      : ChangeSet db.changelog/changes/schema.sql::1::marco ran successfully in 6ms 
Running Changeset: db.changelog/changes/h2-data/001-dev-data.sql::1::marco 
2023-03-27T22:42:27.211+02:00  INFO 68851 --- [           main] liquibase.changelog                      : Custom SQL executed 
2023-03-27T22:42:27.211+02:00  INFO 68851 --- [           main] liquibase.changelog                      : ChangeSet db.changelog/changes/h2-data/001-dev-data.sql::1::marco ran successfully in 6ms 
2023-03-27T22:42:27.215+02:00  INFO 68851 --- [           main] liquibase.lockservice                    : Successfully released change log lock  

When the application runs you can check that 3 tables exists in the h2 console:

schema

Personis our business table, Liquibase created 2 additional tables to manage the scripts, for us is interesting databasechangelog where we can see the changes loaded in the database:
3 tables in the db

The SnakeYML issue

The SnakeYML issue. Liquibase (and SpringBoot) has dependencies with SnakeYML 1.33 that has been affected by a security issue.
snakeyml issue

Some production environments don't allow the deployment of 'unsafe' libraries. For this reason we implemented the code using XML and SQL. Liquibase uses SnakeYML 1.33 as yaml and JSON parser.


You could be interested in

logo of the category: spring-big.svg Spring Boot: REST controller Test example

How to test the @RestController with Spring Boot
2017-10-01

logo of the category: spring-big.svg How to deploy a Java and Angular webapp in one JAR/WAR

How to configure a fullstack Angular and Spring Boot application
2018-04-26
WebApp built by Marco using SpringBoot 3 and Java 21. App hosted in Switzerland.