Quarkus - Using Liquibase

Liquibase is an open source tool for database schema change management.

Quarkus provides first class support for using Liquibase as will be explained in this guide.

Setting up support for Liquibase

To start using Liquibase with your project, you just need to:

  • add your changeLog to the src/main/resources/db/changeLog.xml file as you usually do with Liquibase

  • activate the migrate-at-start option to migrate the schema automatically or inject the Liquibase object and run your migration as you normally do.

In your pom.xml, add the following dependencies:

  • the Liquibase extension

  • your JDBC driver extension (quarkus-jdbc-postgresql, quarkus-jdbc-h2, quarkus-jdbc-mariadb, …​)

  1. <dependencies>
  2. <!-- Liquibase specific dependencies -->
  3. <dependency>
  4. <groupId>io.quarkus</groupId>
  5. <artifactId>quarkus-liquibase</artifactId>
  6. </dependency>
  7. <!-- JDBC driver dependencies -->
  8. <dependency>
  9. <groupId>io.quarkus</groupId>
  10. <artifactId>quarkus-jdbc-postgresql</artifactId>
  11. </dependency>
  12. </dependencies>

Liquibase support relies on the Quarkus datasource config. It can be customized for the default datasource as well as for every named datasource. First, you need to add the datasource config to the application.properties file in order to allow Liquibase to manage the schema.

The following is an example for the application.properties file:

  1. # configure your datasource
  2. quarkus.datasource.db-kind=postgresql
  3. quarkus.datasource.username=sarah
  4. quarkus.datasource.password=connor
  5. quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase
  6. # Liquibase minimal config properties
  7. quarkus.liquibase.migrate-at-start=true
  8. # Liquibase optional config properties
  9. # quarkus.liquibase.change-log=db/changeLog.xml
  10. # quarkus.liquibase.validate-on-migrate=true
  11. # quarkus.liquibase.clean-at-start=false
  12. # quarkus.liquibase.database-change-log-lock-table-name=DATABASECHANGELOGLOCK
  13. # quarkus.liquibase.database-change-log-table-name=DATABASECHANGELOG
  14. # quarkus.liquibase.contexts=Context1,Context2
  15. # quarkus.liquibase.labels=Label1,Label2
  16. # quarkus.liquibase.default-catalog-name=DefaultCatalog
  17. # quarkus.liquibase.default-schema-name=DefaultSchema
  18. # quarkus.liquibase.liquibase-catalog-name=liquibaseCatalog
  19. # quarkus.liquibase.liquibase-schema-name=liquibaseSchema
  20. # quarkus.liquibase.liquibase-tablespace-name=liquibaseSpace

Add a changeLog file to the default folder following the Liquibase naming conventions: src/main/resources/db/changeLog.xml The yaml, json, xml and sql changeLog file formats are also supported.

  1. <?xml version="1.1" encoding="UTF-8" standalone="no"?>
  2. <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  3. xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
  6. http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd
  7. http://www.liquibase.org/xml/ns/dbchangelog
  8. http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
  9. <changeSet author="quarkus" id="1">
  10. <createTable tableName="quarkus">
  11. <column name="ID" type="VARCHAR(255)">
  12. <constraints nullable="false"/>
  13. </column>
  14. <column name="NAME" type="VARCHAR(255)"/>
  15. </createTable>
  16. </changeSet>
  17. </databaseChangeLog>

Now you can start your application and Quarkus will run the Liquibase’s update method according to your config:

  1. import org.quarkus.liquibase.LiquibaseFactory; (1)
  2. @ApplicationScoped
  3. public class MigrationService {
  4. // You can Inject the object if you want to use it manually
  5. @Inject
  6. LiquibaseFactory liquibaseFactory; (2)
  7. public void checkMigration() {
  8. // Get the list of liquibase change set statuses
  9. try (Liquibase liquibase = liquibaseFactory.createLiquibase()) {
  10. List<ChangeSetStatus> status = liquibase.getChangeSetStatuses(liquibaseFactory.createContexts(), liquibaseFactory.createLabels());
  11. }
  12. }
  13. }
1The Quarkus extension provides a factory to initialize a Liquibase instance
2Inject the Quarkus liquibase factory if you want to use the liquibase methods directly

Multiple datasources

Liquibase can be configured for multiple datasources. The Liquibase properties are prefixed exactly the same way as the named datasources, for example:

  1. quarkus.datasource.db-kind=h2
  2. quarkus.datasource.username=username-default
  3. quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default
  4. quarkus.datasource.jdbc.min-size=3
  5. quarkus.datasource.jdbc.max-size=13
  6. quarkus.datasource.users.db-kind=h2
  7. quarkus.datasource.users.username=username1
  8. quarkus.datasource.users.jdbc.url=jdbc:h2:tcp://localhost/mem:users
  9. quarkus.datasource.users.jdbc.min-size=1
  10. quarkus.datasource.users.jdbc.max-size=11
  11. quarkus.datasource.inventory.db-kind=h2
  12. quarkus.datasource.inventory.username=username2
  13. quarkus.datasource.inventory.jdbc.url=jdbc:h2:tcp://localhost/mem:inventory
  14. quarkus.datasource.inventory.jdbc.min-size=2
  15. quarkus.datasource.inventory.jdbc.max-size=12
  16. # Liquibase configuration for the default datasource
  17. quarkus.liquibase.schemas=DEFAULT_TEST_SCHEMA
  18. quarkus.liquibase.change-log=db/changeLog.xml
  19. quarkus.liquibase.migrate-at-start=true
  20. # Liquibase configuration for the "users" datasource
  21. quarkus.liquibase.users.schemas=USERS_TEST_SCHEMA
  22. quarkus.liquibase.users.change-log=db/users.xml
  23. quarkus.liquibase.users.migrate-at-start=true
  24. # Liquibase configuration for the "inventory" datasource
  25. quarkus.liquibase.inventory.schemas=INVENTORY_TEST_SCHEMA
  26. quarkus.liquibase.inventory.change-log=db/inventory.xml
  27. quarkus.liquibase.inventory.migrate-at-start=true

Notice there’s an extra bit in the key. The syntax is as follows: quarkus.liquibase.[optional name.][datasource property].

Without configuration, Liquibase is set up for every datasource using the default settings.

Using the Liquibase object

In case you are interested in using the Liquibase object directly, you can inject it as follows:

If you enabled the quarkus.liquibase.migrate-at-start property, by the time you use the Liquibase instance, Quarkus will already have run the migrate operation.
  1. import org.quarkus.liquibase.LiquibaseFactory;
  2. @ApplicationScoped
  3. public class MigrationService {
  4. // You can Inject the object if you want to use it manually
  5. @Inject
  6. LiquibaseFactory liquibaseFactory; (1)
  7. @Inject
  8. @LiquibaseDataSource("inventory") (2)
  9. LiquibaseFactory liquibaseFactoryForInventory;
  10. @Inject
  11. @Named("liquibase_users") (3)
  12. LiquibaseFactory liquibaseFactoryForUsers;
  13. public void checkMigration() {
  14. // Use the liquibase instance manually
  15. try (Liquibase liquibase = liquibaseFactory.createLiquibase()) {
  16. liquibase.dropAll(); (4)
  17. liquibase.validate();
  18. liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels());
  19. // Get the list of liquibase change set statuses
  20. List<ChangeSetStatus> status = liquibase.getChangeSetStatuses(liquibaseFactory.createContexts(), liquibaseFactory.createLabels()); (5)
  21. }
  22. }
  23. }
1Inject the LiquibaseFactory object
2Inject Liquibase for named datasources using the Quarkus LiquibaseDataSource qualifier
3Inject Liquibase for named datasources
4Use the Liquibase instance directly
5List of applied or not applied liquibase ChangeSets

Configuration Reference