liquibase может быть запущен (среди прочих способов) с помощью servlet listener. А значит, можно автоматизировать запуск liquibase и приурочить проверку/обновление БД к моменту деплоя приложения. Далее в посте рассматривается пример того, как "подружить" liquibase и сервер приложений jboss, настроить эту связку и сделать так, чтобы миграция выполнялась при запуске приложения.
ЗАДАЧА
Итак, пусть у нас имеется приложение, стартуемое на сервере приложений jboss (именно этот сервер выбран исключительно в виде примера; настройка на других серверах ничем особенным не
отличается). Для обеспечения миграции и обновлений базы данных применяется инструмент liquibase. Желательно настроить сервер так, чтобы процесс миграции выполнялся автоматически каждый раз при старте.
LIQUIBASE BEST PRACTICES
Предлагаемые полезные практики по организации работы заключаются в следующем:
- в проекте заводится отдельный модуль/пакет для хранения всех относящихся к liquibase артефактов (файлов изменений, классов с кастомной реализацией процедур миграции и пр.);
- изменения группируются в пакеты; именование пакетов отражает содержимое (например, соответствует id заявок в таск-трекере); пакеты содержат файлы изменений, а также все необходимые классы, если используется CustomChange;
- размещаемый в корне application-liquibase.xml не содержит тэгов changeSet, а только подключает все имеющиеся файлы изменений в правильном порядке (здесь утверждается, что изменения выполняются именно в том порядке, в котором подключены файлы).
Listing 1. Пример структуры пакета
by.father.examples
liquibase
change1
changelog.xml
change2
changelog.xml
change3
changelog.xml
application-liquibase.xml
Подобный подход облегчает процесс работы: выполнены доработки приложения, требующие изменений в БД -> формируется отдельный пакет (с id заявки) с changelog.xml и всеми относящимися к делу артефактами -> этот пакет включается в общий список в определённую последовательность. В результате всегда можно понять, кто, когда, почему и какие изменения внёс.
Listing 2. Содержимое application-liquibase.xml для данного примера
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/2.0 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
<include file="by/father/examples/liquibase/change1/changelog.xml"/>
<include file="by/father/examples/liquibase/change2/changelog.xml"/>
<include file="by/father/examples/liquibase/change3/changelog.xml"/>
</databaseChangeLog>
PUT EVERYTHING IN ITS RIGHT PLACES
- Компилируем и собираем наши артефакты (в данном примере пакет by.father.examples.liquibase заархивирован в application-liquibase.jar).
- Собираем war-архив (в данном примере father-liquibase.war). В WEB-INF\lib\ помещаем наш application-liquibase.jar, а дескриптор развёртывания WEB-INF\web.xml настраиваем следующим образом:
Listing 3. Содержимое WEB-INF\web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<distributable/>
<context-param>
<param-name>liquibase.changelog</param-name>
<!-- размещение общего списка с файлами изменений -->
<param-value>by/father/examples/liquibase/application-liquibase.xml</param-value>
</context-param>
<context-param>
<param-name>liquibase.datasource</param-name>
<!-- ссылка на настроенный датасурс -->
<param-value>datasource</param-value>
</context-param>
<listener>
<!-- класс сервлета -->
<listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
</listener>
</web-app>
- Помещаем father-liquibase.war в каталог deploy\, а liquibase.jar к библиотекам в каталог lib\.
- Стартуем сервис и ищем сообщения об успешной работе:
Listing 4. Записи в логе при успешном старте
INFO [TomcatDeployer] deploy, ctxPath=/father-liquibase, warUrl=.../tmp/deploy/tmp6299631334826712061father-liquibase-exp.war/
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: Successfully acquired change log lock
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: Creating database history table with name: [dbo].[DATABASECHANGELOG]
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: Reading from [dbo].[DATABASECHANGELOG]
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: Reading from [dbo].[DATABASECHANGELOG]
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: ChangeSet by/father/examples/liquibase/change1/changelog.xml::20120815-1::father ran successfully in 0ms
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: ChangeSet by/father/examples/liquibase/change2/changelog.xml::20120815-2::father ran successfully in 0ms
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: ChangeSet by/father/examples/liquibase/change3/changelog.xml::20120816-1::father ran successfully in 0ms
ERROR [STDERR] INFO 16.08.12 13:46:liquibase: Successfully released change log lock
ПРИМЕЧАНИЯ
- По умолчанию liquibase логирует в stderr, т. ч. в логах приложения появляются строки вида "ERROR [STDERR] INFO ... :liquibase: ..." и т. д. Это мешает анализу логов и, например, может вызвать ложное срабатывание системы мониторинга (если она отслеживает логи). Для liquibase 2.x имеется расширение liquibase-slf4j (Slf4j Logging for Liquibase) - архив в 3 килобайта, который нужно просто добавить в classpath. Он перенаправляет вывод на slf4j. Для данного примера будет "INFO [liquibase] ..." как нормальный вывод в лог, безо всякого ERROR.
- Если окажется, что liquibase servlet listener подходит вам по концепции, но по каким-то причинам не подходит по реализации - всегда можно расширить его и сделать, например, class MyLiquibaseServletListener extends liquibase.integration.servlet.LiquibaseServletListener. В этом сервлете можно описать требуемое поведение. Скомплированный класс затем помещается в WEB-INF\classes\, а в дескрипторе web.xml указывается нужный сервлет:<listener-class>by.father.examples.liquibase.MyLiquibaseServletListener</listener-class>
- В процессе миграции данных ряд задач может выполняться продолжительное время. Поэтому запуск таких задач при редеплойменте приложения недопустим в условиях его промышленного использования. Такие задачи должны выполняться в фоновом режиме без остановки сервера приложений в часы его наименьшей загрузки. Определение времени наименьшей загрузки системы и запуск миграции данных выполняется службой поддержки.
ЗАКЛЮЧЕНИЕ
Возможность запуска liquibase через сервлет позволяет автоматизировать и синхронизировать обновление БД с деплоем приложения на сервере, а реализуется это достаточно простыми и понятными настройками.
Комментариев нет:
Отправить комментарий