- Overview
- Running the Project Locally
- Introduction to Liquibase
- Liquibase CLI and Maven
- Liquibase and Spring Boot
- Conclusion
- References
This project demonstrates examples of Liquibase integration with Spring Boot and Maven and how to manage database schema evolution over time.
- JDK 8
- Maven
- Docker (easier to deploy but not mandatory)
- PostgreSQL (mandatory if not using Docker)
Please make sure the above requirements are available on your local machine, please see the references section.
Before running the project please make sure to build the project JAR file using following command
./mvnw clean package
docker-compose up
If docker is not installed on your machine then you can deploy JAR file directly, in that case try to change configuration at
application.properties
file.
java -jar -Dspring.config.location=src/main/resources/application.properties target/spring-liquibase-demo-0.0.1-SNAPSHOT.jar
The project should be up and running on port 8080
on your localhost. This is a simple RESTful web service provides
endpoints to create users and manage their tasks. You can access project's Swagger UI
for more details on REST endpoints.
Liquibase Community is an open source project that helps rapidly track, version, and deploy database schema changes. The following sections will cover basic concepts of Liquibase and how you can integrate Liquibase with Spring boot and Maven.
Liquibase doesn't require any installation using with Spring boot and Maven. But on servers Ant or Maven is not always available. So in this case it's better to use Liquibase CLI and gives more control. Installing Liquibase CLI on Linux/Unix/Mac | Liquibase Docs
Liquibase uses changeset to identify each change to the database. Each changeset has an “id” and “author” attribute which, along with the directory and file name of the changelog file, uniquely identify a change. A changeset can be written in SQL, XML, YAML, and JSON formats.
An example of changeset in SQL format.
--changeset Bob:157
CREATE TABLE person (
id int4 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
firstname varchar (50) NULL,
lastname varchar (50) NOT NULL,
state bpchar (2) NULL,
username varchar (8) NULL,
CONSTRAINT person_pkey PRIMARY KEY (id)
);
--rollback drop table if exists person
Writing changesets using YAML, XML or JSON format allows it to work across databases, e.g. MSSQL and PostgreSQL, unlike SQL changesets which may be database specific.
Below is an example of changeset in YAML format.
databaseChangeLog:
- changeSet:
id: 1
author: bob
changes:
- createTable:
tableName: person
columns:
- column:
name: id
type: int
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: firstname
type: varchar(50)
- column:
name: lastname
type: varchar(50)
constraints:
nullable: false
Liquibase uses a changelog to explicitly list database changes in order. The changelog acts as a ledger of changes and contains a list of changesets (units of change) that Liquibase can execute on a database. Liquibase supports changelogs file also in SQL, XML, YAML, and JSON formats.
Liquibase supports “plain SQL” changelog files. Formatted SQL files use comments to provide Liquibase with metadata. Each SQL changelog file must begin with the following comment:
--liquibase formatted sql
Each changeset in a formatted SQL changelog file begins with a comment of the form:
--changeset author:id attribute1:value1 attribute2:value2 [...]
An example changelog SQL file which contains a set of changes that need to be applied to DB
--liquibase formatted sql
--changeset <author name>:<a unique identifier for the SQL changeset>
<SQL statements go here>
--rollback <rollback SQL statements>
--changeset <author name>:<another unique identifier>
<SQL statements go here>
--rollback <rollback SQL statements>
changeset and changelog in SQL format seem most convenient for writing and debugging if database agnostic in not a goal
See more
0000_initial_database_setup.sql
0010_add_some_tables.sql
0020_create_some_procedures.sql
0030_you_can_also_have_xml_changelogs.xml
By using an initial four-digit number, the files will get sorted in a predictable order. As developers add more changesets, leave some ‘space’ in the numbering sequence in case we ever need to add a new changeset in between two existing changesets. Notice that we can also include changelogs in other formats.
Each changeset inside a changelog file can be prefixed by the 4-digit number joining an underscore with the sequence number of the changeset in that file.
--changeset Bob:0000_1
<SQL changes ….>
--changeset Bob:0000_2
<SQL changes ….>
These are not any rules imposed by Liquibase but can help to achieve a nice organization in versioning.
We can maintain a master changelog file to point all the changelog files for migrations. Below is an example of a master changelog file where we include all changelogs in sorted order by name. We can also include changelogs in a custom order by using include tag.
databaseChangeLog:
- includeAll:
path: changes
relativeToChangelogFile: true
A liquibase.properties file is a text-based file that allows one to store properties that don't change often. We can typically use this file to specify our database connection information and sensitive data that Liquibase will use. We can even maintain separate properties file like liquibase-.properties file for different environments.
An example of liquibase properties file
changeLogFile=src/main/resources/db/changelog/db.changelog-master.yaml
url=jdbc:postgresql://<host>:5432/<db_name>
username=<username>
password=<password>
driver=org.postgresql.Driver
contexts=dev
See more on Creating and Configuring Liquibase.Properties Files | Liquibase Docs
You can easily integrate Liquibase with Maven using plugin to run migrations smoothly with maven commands.
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>4.0.0</version>
<configuration>
<propertyFileWillOverride>true</propertyFileWillOverride>
<propertyFile>/path/to/propertiesfile</propertyFile>
</configuration>
</plugin>
While it's convenient to use Liquibase auto migration with Spring boot, but there is no easy way for rollbacks. That's why Maven integration is a good choice for other options. We can keep both in a project.
See more on Setting up a Liquibase project with Maven and PostgreSQL
Update changes to the database that need to be applied
mvn liquibase:update -Dliquibase.contexts=dev -DliquibasePropertiesFile=src/main/resources/db/liquibase-dev.properties
Rollback can be performed on different parameters. The easiest one is to revert the last n changes.
mvn liquibase:rollback -Dliquibase.rollbackCount=<n> -DliquibasePropertiesFile=src/main/resources/db/liquibase-dev.properties
See more on Commands | Liquibase Docs
Spring Boot provides integration with Liquibase that gets autoconfigured when the Liquibase dependency is added to the POM, and runs migrations automatically on application startup. All you need is to put your change log in “db/changelog/db.changelog-master.yaml” for master changelog and add the "liquibase-core" dependency into the pom.xml.
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.0.0</version>
</dependency>
Default behaviors can be changed and configured through application.properties. For example to disable liquibase migration
you can make spring.liquibase.enabled
property value to false.
spring.liquibase.enabled=false
This is not a complete guide on Liquibase, please refer to the official liquibase doc if you’re interested to learn more about a specific scenario. Liquibase is based on Martin Fowler’s Evolutionary Database - an article that covers the concepts and practices of Evolutionary Database Architecture.