MyTetra Share
Делитесь знаниями!
Flyway: управление миграциями баз данных SQL (версионирование, изменение структуры)
Время создания: 25.12.2012 11:55
Текстовые метки: база данных, версионность, версия, версионирование
Раздел: Компьютер - Программирование - SQL - MySQL, MariaDB
Запись: xintrea/mytetra_syncro/master/base/1356422150wukzn3zlbt/text.html на raw.github.com

В этой статье я расскажу об одном из средств обеспечения версионности схем и управления миграциями БД — библиотеке Flyway. С поблемой версионности схемы базы данных рано или поздно приходится сталкиваться разработчикам любого приложения, опирающегося на СУБД. Увы, иногда эта проблема принимается в рассмотрение слишком поздно — например, если вопрос о внесении изменений в структуру базы встаёт, когда приложение уже находится в эксплуатации. Но и на этапе разработки контроль схемы базы данных причиняет не меньше проблем, чем все прочие аспекты версионности приложения: в отсутствие чёткой системы управления миграциями локальная, стендовая и эксплуатационная базы могут быстро «разъехаться», не предоставляя при этом никакой информации относительно своего текущего состояния.

Перзистенс-провайдеры штатно позволяют лишь в том или ином виде экспортировать актуальную объектную модель в виде схемы базы данных. Этот процесс может быть выполнен в режиме пересоздания (с полным удалением всей структуры), обновления (с внесением изменений) или сверки (без внесения изменений). Например, в Hibernate это делается с помощью инструмента hbm2ddl, работа которого может быть настроена единственным конфигурационным параметром в файле hibernate.cfg.xml или persistence.xml. Однако пересоздание (режим create) бывает нежелательным, если в базе уже есть данные, а обновление (режим update) вносит не все изменения, а только недеструктивные (например, не удаляются столбцы и таблицы) и не учитывает требующуюся реструктуризацию данных. Зачастую, если модель данных претерпела множество изменений, применить их к эксплуатационной базе бывает непросто, особенно если текущая версия базы неизвестна. Так или иначе, но приходится «опускаться» до SQL-скриптов — тут-то и встаёт вопрос управления версионностью.

Flyway

На главной странице проекта приведена наглядная таблица сравнения библиотеки с аналогичными решениями, и здесь основное внимание хочется обратить на богатую функциональность, работу с миграциями в виде простых SQL-файлов или Java-классов (последние по сути основываются на Spring JDBC Template) и поддержку нативного SQL популярных СУБД (Oracle PL/SQL, SQL Server T/SQL, хранимые процедуры MySQL и PostgreSQL).

Flyway хорошо интегрируется с Ant, Maven и инструментами командной строки, имеет API для программного вызова и интеграцию со Spring, работает со множеством СУБД. Я приведу пример подключения Flyway к уже существующему проекту, сборка которого основывается на Maven, а вызов Flyway производится при старте контекста Spring. В качестве базы данных в проекте используется MySQL.

Подключение Flyway к проекту

Для начала создадим папку db/migration в подкаталоге src/main/resources проекта: в ней будут храниться скрипты миграции. Поместим туда предварительно экспортированный скрипт базы данных — со всеми таблицами, представлениями, индексами и т.д. Назовём файл V1__Base_version.sql. Подробно соглашения по именованию миграций описаны в документации, пока достаточно сказать, что имя файла начинается с V, далее следует номер версии (с произвольным количеством точек-разделителей), двукратный символ подчёркивания и описание миграции.

Добавим в зависимости проекта (раздел dependencies) ядро библиотеки Flyway:

<dependency>

<groupId>com.googlecode.flyway</groupId>

<artifactId>flyway-core</artifactId>

<version>1.5</version>

</dependency>

А в сборочные плагины (раздел build/plugins) — плагин Flyway:

<plugin>

<groupId>com.googlecode.flyway</groupId>

<artifactId>flyway-maven-plugin</artifactId>

<version>1.5</version>

<configuration>

<driver>com.mysql.jdbc.Driver</driver>

<url>jdbc:mysql://localhost:3306/flywaytest?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;connectionCollation=utf8_general_ci&amp;characterSetResults=UTF-8</url>

<baseDir>db/migration</baseDir>

</configuration>

</plugin>

Для запуска Flyway через плагин лучше создать отдельную учётную запись в базе. Можно указать пользователя и пароль для подключения к базе здесь же, в конфигурации плагина:

<configuration>

<user>flyway</user>

<password>mySecretPassword</password>

...

</configuration>

Или в параметрах командной строки:

-Dflyway.user=flyway -Dflyway.password=mySecretPwd

Но более удобным способом, в случае сборки на Maven, будет помещение типовых параметров в файл настроек Maven (файл settings.xml) и дальнейшее использование их во всех аналогичных проектах:

<servers>

<server>

<id>flyway-db</id>

<username>flyway</username>

<password>mySecretPassword</password>

</server>

</servers>

Если необходимо инициализировать текущую базу с нуля, то можно выполнить её очистку. При этом всё содержимое базы будет удалено:

mvn flyway:clean

При успешном выполнении задачи база окажется пустой, а в логе Maven появятся следующие строки:

[INFO] --- flyway-maven-plugin:1.5:clean (default-cli) @ flyway-test-project ---

[INFO] Cleaned database schema 'flywaytest' (execution time 00:03.911s)

Если же база находится в актуальном состоянии (соответствует выгруженному ранее скрипту), необходимо выполнить задачу, которая создаст в ней необходимую для поддержания версионности структуру:

mvn flyway:init -Dflyway.initialVersion=1 -Dflyway.initialDescription="Base version"

Далее можно убедиться, что в базе появилась таблица schema_version с единственной записью, соответствующей текущему состоянию базы:

Интеграцию Flyway с приложением выполним в виде бина Spring, стартующего перед entityManagerFactory:

<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate">

<property name="dataSource" ref="..."/>

...

</bean>

<!-- Ставим фабрику менеджеров сущностей в зависимость от Flyway, чтобы убедиться, что она будет выполнена после внесения изменений в базу -->

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory" depends-on="flyway">

...

</bean>

После запуска приложения на чистой базе она будет инициализирована скриптом V1__Base_version.sql, кроме того, будет создана таблица schema_version. В логе при этом можно наблюдать следующее:

2012-04-04 06:42:09,279 INFO [com.googlecode.flyway.core.metadatatable.MetaDataTable] -- <Metadata table created: schema_version (Schema: flywaytest)>

2012-04-04 06:42:09,318 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Current schema version: null>

2012-04-04 06:42:09,320 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Migrating to version 1>

2012-04-04 06:42:24,897 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Successfully applied 1 migration (execution time 00:15.615s).>

Если же приложение было запущено на базе, идентичной последней миграции, то никаких изменений в схеме не произойдёт, что будет отражено в логе приложения следующими строками:

2012-04-04 06:36:14,081 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Current schema version: 1>

2012-04-04 06:36:14,085 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Schema is up to date. No migration necessary.>

В любом случае, при корректной интеграции Flyway база данных должна содержать приведённую выше таблицу schema_version с единственной записью.

Создание миграции

Создадим в папке db/migration файл с названием V2__Test_change.sql и со следующим содержимым:

create table test_table (

id bigint(20) not null,

primary key(id)

);

После запуска приложения обнаружим в логе следующие строки:

2012-04-04 06:51:02,708 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Current schema version: 1>

2012-04-04 06:51:02,710 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Migrating to version 2>

2012-04-04 06:51:03,137 INFO [com.googlecode.flyway.core.migration.DbMigrator] -- <Successfully applied 1 migration (execution time 00:00.480s).>

И убедимся, что таблица test_table была успешно создана, а в таблице schema_version появилась запись о применённой миграции:

Откат миграции

Flyway, в отличие, например, от системы миграции в Rails, не поддерживает откат изменений. Авторы библиотеки мотивируют это тем, что после внесения деструктивных и необратимых изменений выполнить откат состояния базы так, чтобы все пропавшие или изменившиеся данные восстановились к прежнему состоянию, в общем случае невозможно. Вместо этого предлагается вполне разумный подход использования механизмов резервирования. Например перед применением очередной миграции можно делать выгрузку дампа или снимок базы (в зависимости от имеющегося в конкретной СУБД функционала резервирования).

Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования