Вы решили прикоснуться к будущему и выбрали для своего нового проекта Quarkus. Поздравляю, и сочувствую одновременно. На данный момент в кваркусе множество различных недоработок, которые решаются различными костылями, и сегодня мы разберем одну из таких проблем.
Обычно в проектах с базой данных используют систему миграций, например Liquibase. Но использовать Liquibase вместе с реактивным драйвером Postgres попросту не получится, так как Liquibase не поддерживает такие драйверы на данный момент (GitHub Issue). Но есть способ обойти эту проблему.
Возможно вас озарила отличная идея: будем использовать Flyway вместо Liquibase. Спешу вас расстроить, у Flyway тоже есть проблема (GitHub Issue).
Используемые версии
Java: 17
Quarkus: 2.8.1
Postgres: 13
Для работы Liquibase в Quarkus c Postgresql нам нужны всего две зависимости:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-liquibase</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
Создадим главный changeLog файл с простой миграцией, создадим одну таблицу.
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="first_name" type="varchar(64)"/>
</createTable>
</changeSet>
</databaseChangeLog>
Также необходимо в файл application.properties
добавить строку, которая запустит миграции при старте сервиса. Без нее Liquibase не будет работать.
quarkus.liquibase.migrate-at-start=true
По умолчанию quarkus ищет миграции в файле resource/db/changeLog.xml
. Но вы можете установить свой путь до файла, используя параметр quarkus.liquibase.change-log
.
Тперь мы можем запустить наше приложение... И ничего не происходит. Приложение запущено, но никакие скрипты миграции не были выполнены. Хотя, если вы сделаете то же самое с использованием JDBC драйвера, то все будет работать.
Решение проблемы
Есть несколько решений данной проблемы. Вы можете создать отдельное приложение с миграциями, или запускать контейнер с Liquibase перед запуском приложения. О таких способах запуска я рассказывал в другой статье.
Второе решение мы рассмотрим сейчас. При старте приложения, мы будем руками создавать соединение с базой данных, используя обычный, Postgres JDBC Client. И через это соединение накатывать схему.
Для этого создаем новый класс конфигурации LiquibaseConfig
:
@Singleton
public class LiquibaseConfig {
private static final Logger log = LoggerFactory.getLogger(LiquibaseConfig.class);
@ConfigProperty(name = "quarkus.datasource.jdbc.url")
String datasourceUrl;
@ConfigProperty(name = "quarkus.datasource.username")
String datasourceUsername;
@ConfigProperty(name = "quarkus.datasource.password")
String datasourcePassword;
@ConfigProperty(name = "quarkus.liquibase.change-log", defaultValue = "db/changeLog.xml")
String changeLogLocation;
@ConfigProperty(name = "quarkus.liquibase.clean-at-start")
boolean cleanAtStart;
public void runLiquibaseMigration(@Observes StartupEvent event) {
final ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader());
try (final Liquibase liquibase = new Liquibase(
changeLogLocation,
resourceAccessor,
DatabaseFactory.getInstance()
.openConnection(datasourceUrl, datasourceUsername, datasourcePassword, null, resourceAccessor))
) {
if (cleanAtStart) {
log.warn("Liquibase drop database");
liquibase.dropAll();
}
liquibase.update(new Contexts(), new LabelExpression());
} catch (LiquibaseException e) {
log.error("Liquibase error: {}", e.getMessage());
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
С помощью @ConfigProperty
мы передаем необходимые параметры для миграции. Если установить значение true
в переменной cleanAtStart
, то при каждом запуске база данных будет очищаться, а схема накатываться заново.
Наш метод runLiquibaseMigration
запустится после запуска приложения, благодаря параметру метода @Observes StartupEvent event
.
Также необходимо добавить в зависимости обычный драйвер для базы данных.
И необходимо изменить файл application.properties
:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_liquibase
quarkus.datasource.jdbc=false
quarkus.datasource.username=postgres
quarkus.datasource.reactive.url=postgresql://localhost:5432/quarkus_liquibase
quarkus.datasource.password=
quarkus.liquibase.migrate-at-start=true
Не смотря на то, что мы отключаем использование jdbc
, мы указываем параметр quarkus.datasource.jdbc.url
. Он будет использоваться только для миграций, и не вызовет конфликтов с реактивным драйвером.
Резюмирую
Пока нативно запустить Liquibase при старте реактивного Quarkus сервиса не получается, можно использовать хак с запуском обычного драйвера базы данных.