В этом гайде мы расскажем, как подключить и настроить Liquibase в Spring Boot приложении, сгенерировать скрипты инициализации и миграции схемы БД, а также дополнить уже существующие changelog файлы новыми скриптами миграции вместе с Amplicode!
Примерное время прочтения: 20 минут.
В процессе разработки приложения нам время от времени приходится вносить изменения в JPA модель, что влечет за собой эволюцию схемы БД.
Без использования системы контроля версий существует риск потери данных и возникновения сложностей при совместной работе нескольких разработчиков над проектом. На сегодняшний день наиболее распространенной системой контроля версий является git, который отлично подходит для отслеживания изменений во всех файлах проекта.
Однако, помимо кода, не менее важно версионировать и базу данных, чтобы избежать потери данных при изменениях и иметь возможность отката к предыдущим версиям схемы БД в случае неудачного обновления или ошибки в процессе разработки.
Для эффективного управления версиями базы данных существуют специализированные решения, такие как Liquibase.
Данный гайд посвящен использованию Liquibase в Spring Boot приложениях с помощью Amplicode для наиболее удобного и эффективного управления базами данных.
Для прохождения этого гайда самостоятельно в IntelliJ IDEA, можно перейти в репозиторий с гайдами и выполнить шаги, описанные в инструкции, чтобы настроить проект.
Прежде чем приступать к решению поставленных задач, необходимо изучить структуру используемого приложения
BlogApplicaion
с помощью панели Amplicode Explorer.
Панель выглядит следующим образом:
Чтобы проанализировать приложение в контексте используемых фреймворков и библиотек с помощью панели Amplicode Explorer, нажмите на значок стрелочки рядом с именем проекта, чтобы развернуть его. Здесь мы можем узнать, какие модули подключены к нашему проекту, что из себя представляет слой данных, какие эндпонты доступны, а также какие файлы для развертывания приложений уже есть в проекте.
Исходя из знаний Amplicode о нашем проекте, мы можем сделать следующие выводы:
BaseEntity
(3.1)Post
и
User
связаны отношением "многие к одному" (3.2)docker-compose.yaml
файл и его элементы (4)Первая задача — это подключение и настройка Liquibase к Spring Boot приложению с уже разработанной ранее JPA моделью. В процессе решения этой задачи, помимо добавления необходимой зависимости и настройки файла
application.properties
, нам также потребуется создать скрипт инициализации базы данных.
Для взаимодействия с PostgreSQL было бы удобно иметь также сервис pgAdmin. Amplicode предлагает pgAdmin в списке рекомендуемых сервисов, так как видит сервис PostgreSQL в текущем файле. Чтобы воспользоваться этой возможностью, нажмите на иконку в виде лампочки и выберите Add pgAdmin service в появившемся меню:
Откроется всплывающий диалог Add pgAdmin to Docker Compose. Чтобы воспользоваться предоставленной Amplicode возможностью для настройки автоматического подключения PostgreSQL к pgAdmin:
Amplicode сгенерирует следующий код:
pgadmin:
image: dpage/pgadmin4:8.6
restart: always
ports:
- "5050:80"
volumes:
- pgadmin_data:/var/lib/pgadmin
- ./docker/pgadmin/servers.json:/pgadmin4/servers.json
- ./docker/pgadmin/pgpass:/pgadmin4/pgpass
environment:
PGADMIN_DEFAULT_EMAIL: admin@admin.com
PGADMIN_DEFAULT_PASSWORD: root
PGADMIN_CONFIG_SERVER_MODE: "False"
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:80/misc/ping || exit -1
interval: 10s
timeout: 5s
start_period: 10s
retries: 5
entrypoint: /bin/sh -c "chmod 600 /pgadmin4/pgpass; /entrypoint.sh;"
volumes:
pgadmin_data:
Теперь необходимо запустить все сервисы, чтобы посмотреть на актуальное состояние БД. Для этого необходимо нажать на иконку двойной стрелочки напротив слова
services
в файле
docker-compose.yaml
После запуска Amplicode добавит inlay для удобного открытия адреса, связанного с сервисом, прямо из IntelliJ IDEA.
На данный момент база данных находится в том же состоянии, что и JPA модель. Переходим к подключению Liquibase к нашему приложению.
Amplicode Explorer позволяет добавить необходимые стартеры и библиотеки к проекту. Для этого необходимо:
Откроется диалоговое окно DB Migration Settings.
Далее необходимо сделать следующее:
Благодаря выбору первого чекбокса произойдет автоматическое перенаправление к окну генерации скрипта базы данных. Благодаря выбору второго чекбокса Amplicode выполнит команду Liquibase
changelog-sync
сразу после генерации скриптов инициализации базы данных. Эта команда позволит отметить скрипты как выполненные, при этом фактически их не выполняя, т.к. все, что будет в них описано, уже есть в базе данных.
(Здесь необходимо отметить, что Amplicode позволяет создать подключение к базе данных с нуля, либо отталкиваясь от информации, указанной для источника данных в приложении. Здесь следует выбрать второй вариант, так как источник данных в приложении уже настроен.)
Появится следующее диалоговое окно:
Нажмите Test Connection, чтобы проверить подключение. После этого нажмите OK.
Нажмите OK также в главном диалоговом окне.
Amplicode добавит необходимые зависимости в
build.gradle
и свойства в
application.properties
, сгенерирует файл
db.changelog-master.xml
и начнет процесс генерации скрипта инициализации базы данных.
По результатам выполнения Amplicode сохранит changelog по указанному пути и выполнит команду
changelog-sync
, т.к. ранее был отмечен соответствующий чекбокс.
По итогу выполненных действий команда будет успешно выполнена, системные таблицы Liquibase будут добавлены в базу данных, а таблица
databasechangelog
получит информацию о выполненных скриптах. Выполнение первой задачи на этом завершено.
Приступим к модификации текущей JPA модели. Так как использование факса стало редкостью, следует убрать его из модели, удалив следующий (выделенный) код, а также связанные с ним геттер и сеттер:
Для обеспечения полноты данных сделаем атрибут
email
обязательным с помощью Amplicode Designer.
Также нам необходимо добавить новую сущность -
Comment
и установить отношение "многие ко многим" с сущностью
Post
.
Чтобы выполнить оба эти действия с помощью Amplicode Designer:
Post
и откройте окно создания ассоциации двойным щелчком на пункте меню Attributes -> Association в палитреSet
, но для полностью корректной работы
Set
с JPA сущностями также необходимо чтобы у неё были корректно переопределены методы
equals()
и
hashCode()
. Amplicode знает об этом, и предупреждает, что текущая реализация может быть неоптимальной в плане производительности и предложит сгенерировать реализацию методов
equals()
и
hashCode()
(2). Чтобы согласиться с этим предложением, нажмите соответствующую ссылку (3).Comment
.Amplicode сгенерирует следующий код для сущности:
@Entity
@Table(name = "comment")
public class Comment extends BaseEntity {
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
Class<?> oEffectiveClass = o instanceof HibernateProxy
? ((HibernateProxy) o).getHibernateLazyInitializer()
.getPersistentClass()
: o.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy
? ((HibernateProxy) this).getHibernateLazyInitializer()
.getPersistentClass()
: this.getClass();
if (thisEffectiveClass != oEffectiveClass) return false;
Comment comment = (Comment) o;
return getId() != null && Objects.equals(getId(), comment.getId());
}
@Override
public final int hashCode() {
return this instanceof HibernateProxy
? ((HibernateProxy) this).getHibernateLazyInitializer()
.getPersistentClass()
.hashCode()
: getClass().hashCode();
}
}
Теперь необходимо сгенерировать для сущности
Comment
новый базовый атрибут
text
, а также создать отношение "многие к одному" с сущностью
User
. Для этого:
Comment
в соответствующем файлеAmplicode сгенерирует код атрибута, а также getter и setter к нему:
@Column(name = "text")
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
User
author
) и сделайте его обязательным.Amplicode сгенерирует код атрибута, а также getter и setter к нему:
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "author_id", nullable = false)
private User author;
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
Задача по модификации JPA модели выполнена. Следующий шаг — генерации скриптов миграции Liquibase.
Для создания Liquibase скрипта миграции необходимо обратиться к панели Amplicode Explorer и в секции DB Versioning выбрать пункт Liquibase Diff Changelog.
В открывшемся окне следует убедиться в правильности выбранных persistence unit и подключения к базе данных.
С окном предпросмотра changelog файлов мы уже познакомились в процессе создания скрипта инициализации базы данных.
Стоит отметить, что в данном случае цветовая раскраска некоторых changeset отличается, например, скрипт на удаление колонки
fax
отмечен красным цветом, а скрипт на добавление
NotNull
ограничения — желтым.
Оба скрипта являются потенциально опасными ввиду возможной потери данных, поэтому Amplicode старается обратить на них максимальное внимание со стороны разработчика.
Разрабатывая JPA модель, мы решили две бизнес задачи: изменение сущности
User
и создание сущности
Comment
. Нам бы не хотелось складывать скрипты, относящиеся к разным бизнес задачам, в один файл. Amplicode позволяет разнести changeset скрипты в несколько changelog файлов, не покидая текущего окна.
Для этого необходимо:
users
изменить путь к файлу (1), задать имя (2) и указать главный changelog (3), в который нужно будет включить текущий файл.Также обратите внимание, что для скрипта добавления
NotNull
ограничения Amplicode хочет предложить некоторые улучшения и сигнализирует нам об этом иконкой лампочки. Так как мы добавляем
NotNull
ограничение, мы должны быть уверены в том, что в таблице уже нет
null
значений. Amplicode позволяет нам улучшить существующий скрипт, указав значение, которое будет проставлено для всех
null
значений перед добавлением ограничения.
Перед сохранением changelog файлов можем еще раз убедиться в их корректности в окне предпросмотра.
Кстати, есть еще один скрипт, который мы упустили. Это скрипт удаления индекса из таблицы
Users
в секции Ignored.
Amplicode автоматически разместил этот скрипт в данную секцию, т.к. создание индекса может быть довольно дорогостоящей операцией, а его добавление на уровне JPA модели не является довольно распространенным. Поэтому в реальной жизни удалять его из базы данных приходится крайне редко.
Но в случае необходимости всегда можно удалить скрипт из списка игнорируемых, либо наоборот расширить список игнорируемых скриптов, выбрав и перетащив ненужные скрипты в секцию Ignored прямо в окне предпросмотра или сконфигурировав такой список заранее в настройках.
Оба changelog файла успешно сгенерированы и включены в наш основной файл
db.changelog-master.xml
.
Остается только выполнить скрипты и накатить изменения в базу данных. Конечно же, самым правильным подходом к применению скриптов миграции будет настройка Liquibase плагина для Maven или Gradle, т.к. он в любом случае понадобится во время настройки CI/CD pipeline. Но, чтобы не отвлекаться на эту задачу на данном этапе, в рамках данного гайда можно воспользоваться действием Liquibase Update в Amplicode и накатить скрипты без предварительной настройки плагина, отложив эту задачу на потом.
При выборе данного пункта из меню появится следующее выплывающее окно. Нажмите в нам кнопку Update.
Все скрипты выполнились успешно, а информация об их успешном выполнении была записана в таблицу
databasechangelog
.
Модифицируя JPA модель, мы забыли сделать атрибут
text
для сущности
Comment
обязательным.
Исправить эту оплошность довольно легко.
Для этого, воспользуйтесь панелью Amplicode Designer и пометьте атрибут как обязательный:
Однако, засорять проект множеством Liquibase changelog файлов нежелательно. К тому же, данный скрипт относится именно к changelog файлу, содержащему скрипты, связанные с созданием таблицы
Comments
. Благодаря тому, что Liquibase исполняет и фиксирует исполнение именно changeset'ов, из которых состоит changelog файл, а не исполнение всего changelog файла целиком, мы можем дополнить существующие changelog файлы новыми changeset скриптами. Amplicode хорошо знает про подобную возможность и позволяет догенерировать необходимые скрипты в существующий файл.
Для этого:
<changeSet id="1718798931904-1" author="georgii (generated)">
<addNotNullConstraint columnDataType="VARCHAR(255)" columnName="text" tableName="comment" validate="true"/>
</changeSet>
databasechangelog
через pgAdmin.Хорошей практикой при разработке Spring Boot приложения и использованием системы версионирования баз данных является применение возможностей валидации соответствия JPA модели и схемы базы данных при помощи Hibernate. Путем использования свойства Hibernate
spring.jpa.hibernate.ddl-auto
со значением
validate
мы можем обеспечить соответствие JPA модели и схемы базы данных.
Внесите следующий код в файл
application-properties
:
spring.jpa.hibernate.ddl-auto=validate
Чтобы сделать это быстро, начните печатать
ddl-auto
и Amplicode предложит вам соответствующее свойство. Достаточно будет выбрать его из выпадающего списка и нажать
Enter
.
В случае обнаружения несоответствия Spring Boot приложение не запустится и выдаст ошибку.
Запустите приложение, чтобы убедиться, что в логах действительно отсутствуют исключения.
Если приложение запущено без проблем, лог должен выглядеть примерно следующим образом:
Столь же важно отметить, что в случае несоответствия JPA модели и схемы базы данных Amplicode сообщил бы о проблеме и дал бы возможность решить ее прямо из stacktrace.
Подводя итог, все поставленные в данном гайде задачи успешно выполнены примерно за 20 минут.
Вы научились:
Вы можете установить Amplicode уже сейчас и применить полученные знания на практике.