Liquibase is a popular library for versioning and deploying database changes. This is an open source library and widely use in the Java ecosystem or as part of a Jenkins build.
Manual SQL schema updates are commonly used but they are not recommended if you want to keep track of the changes in your database. With Liquibase you can know what changes have been applied and when they were made so you could easily roll back the SQL schema to a previous state.
The Internals
Liquibase simply runs SQL commands and adds records of the changes on a table named DATABASECHANGELOG
which is responsible for keeping track of all the changes on a particular database. Every time a new change is applied through Liquibase a new record is added to the DATABASECHANGELOG
.
Each change log record has its own MD5 hash and they are checked against the Liquibase changeset files when loading the Liquibase configuration. If a change is made on a changeset that is already applied, Liquibase will interrupt the execution and log a validation error in order to prevent modifying the history of changes.
How to Use
We are going to use Liquibase in combination with Maven and Spring.
-
Create a Spring Boot project at the Spring initializr site.
-
Add the required dependencies to your
pom.xml
file. You will need at least three dependencies: the database connector (e.g.postgresql
,mysql
…), the JPA dependency and the liquibase dependency.<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> </dependency>
-
Add the database configuration to your
application.properties
orapplication.yml
. e.g.:spring.datasource.url=jdbc:postgresql://localhost:5432/my-database spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.username=postgres spring.datasource.password=postgres
-
Create a master change set file (
db.changelog-master.yaml
) in the default Liquibase changelog folder (src/main/resources/db/changelog
).databaseChangeLog: - include: file: db/changelog/changeset/document-table.yaml - include: file: db/changelog/changeset/person-table.yaml
You can organize you change sets in different files, for example, one for each SQL table.
e.g.
src/main/resources/db/changelog/changeset/document-table.yaml
:databaseChangeLog: - changeSet: id: craete-document-table author: Sergio Martin Rubio changes: - createTable: tableName: document columns: - column: name: customer_id type: varchar(255) constraints: primaryKey: true nullable: false - column: name: document_url type: varchar(255) constraints: nullable: false
-
Get your database up and running. For example you can use Docker to spin up a PostgreSQL database:
version: '3' services: postgres: container_name: postgres image: "postgres:latest" ports: - "5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: my-database
-
Run your Spring Boot application from the command line with
./mvnw spring-boot:run
Liquibase also provides an API to configure things like the location of the master changeset file (
spring.liquibase.change-log
). A full list of all the properties can be found on the Spring documentation.
Liquibase Directives
Some of the most common Liquibase directives are:
-
- changeSet: id: craete-person-table author: Sergio Martin Rubio changes: - createTable: tableName: person columns: - column: name: id type: varchar(255) constraints: primaryKey: true nullable: false - column: name: customer_id type: varchar(255) constraints: nullable: false foreignKeyName: fk_person_document references: document(customer_id) - column: name: last_name type: varchar(255) constraints: nullable: false - column: name: first_name type: varchar(255) constraints: nullable: true - column: name: aliases type: varchar(255) constraints: nullable: true - column: name: category type: varchar(255) constraints: nullable: true - column: name: position type: varchar(255) constraints: nullable: true - column: name: keywords type: varchar(255) constraints: nullable: true - column: name: entered type: date constraints: nullable: true - column: name: updated type: date constraints: nullable: true
-
- changeSet: id: change-aliases-column-type author: Sergio Martin Rubio changes: - modifyDataType: columnName: aliases newDataType: clob tableName: person
-
- changeSet: id: add-primary-key author: Sergio Martin Rubio changes: - addPrimaryKey: columnNames: id tableName: person
-
- changeSet: id: drop-primary-key author: Sergio Martin Rubio changes: - dropPrimaryKey: dropIndex: true tableName: person
-
- changeSet: id: add-foreign-key author: Sergio Martin Rubio changes: - dropForeignKeyConstraint: baseColumnNames: customer_id baseTableName: person constraintName: fk_person_document referencedColumnNames: customer_id referencedTableName: customer
-
- changeSet: id: drop-foreign-key author: Sergio Martin Rubio changes: - dropForeignKeyConstraint: baseTableName: person constraintName: fk_person_document
A full list of changes that can be applied can be found on the official Liquibase documentation.