Когда-то я представлял вашему вниманию инструмент для управления изменениями в БД - liquibase. Теперь я решил описать как его можно использовать в проектах с maven-сборкой, и hibernate.
Для начала необходимо создать отдельный maven-артефакт, который будет содержать только ресурсы - xml-файлы с changeSet-ами от liquibase. Структура файлов в папке resources нашего артефакта следующая:
/changelogs/версия_проекта/*-changelog.xml
/changelogs/версия_проекта/release-changelog.xml
/project-changelogs.xml
Файл project-changesets.xml включает в себя набор файлов release-changelog.xml всех версий проекта, вот пример его содержимого:
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">
<include file="changelogs/1.0/release-changelog.xml" relativeToChangelogFile="true"/>
<include file="changelogs/1.1/release-changelog.xml" relativeToChangelogFile="true"/>
<include file="changelogs/2.0/release-changelog.xml" relativeToChangelogFile="true"/>
...
</databaseChangeLog>
Таким же образом файлы release-changelog.xml включают в себя список файлов с измененями, находящихся в той же папке и касающихся лишь данной версии проекта. Сами изменения я предпочитаю описывать не пользуясь при это тэгами liquibase, а использую привычный SQL.
Благодаря наличию в liquibase такого понятия как "контекст", можно разделить все операции над БД на три контекста:
schemedata - контекст в котором будут отражены изменения структруры базы;
initdata - контекст определяющий данные для инициализации БД, например добавление новых пользовательских ролей;
migratedata - контекст используемый при изменении "живых" данных, т.е данных появляющихся в системе при ее эксплуатации.
Такая концепция позволит вам четко разделять виды изменений в БД. А также "накатывать" изменения лишь нужных контекстов. Пример:
<changeSet id="2" author="nkoksharov" context="initdata">
<sql>
insert into roles values (123, 'ROLE_USER');
</sql>
<rollback>
<sql>
delete from roles where id = 123;
</sql>
</rollback>
</changeSet>
Если в проекте предполагается использовать две базы - БД разработчика/продакшн и БД для тестов, то pom-файл, созданного нами артефакта, будет содержать следующие строки:
<project>
<properties>
<liquibase.goal>update</liquibase.goal>
</properties>
<profiles>
<profile>
<id>devdb</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<database.url>${app.database.url}</database.url>
<database.user>${app.database.user}</database.user>
<database.password>${app.database.password}</database.password>
<database.contexts>${app.database.contexts}</database.contexts>
</properties>
</profile>
<profile>
<id>testdb</id>
<properties>
<database.url>${test.database.url}</database.url>
<database.user>${test.database.user}</database.user>
<database.password>${test.database.password}</database.password>
<database.contexts>${test.database.contexts}</database.contexts>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-plugin</artifactId>
<version>1.9.3.0</version>
<configuration>
<changeLogFile>src/main/resources/project-changelogs.xml</changeLogFile>
<driver>org.postgresql.Driver</driver>
<url>${database.url}</url>
<username>${database.user}</username>
<password>${database.password}</password>
<contexts>${database.contexts}</contexts>
</configuration>
<executions>
<execution>
<id>appdb</id>
<phase>process-resources</phase>
<configuration>
<contexts>${app.database.contexts}</contexts>
<url>${app.database.url}</url>
<username>${app.database.user}</username>
<password>${app.database.password}</password>
</configuration>
<goals>
<goal>${liquibase.goal}</goal>
</goals>
</execution>
<execution>
<id>testdb</id>
<phase>process-resources</phase>
<configuration>
<contexts>${test.database.contexts}</contexts>
<url>${test.database.url}</url>
<username>${test.database.user}</username>
<password>${test.database.password}</password>
</configuration>
<goals>
<goal>${liquibase.goal}</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
Параметры app.database.xxx и test.database.xxx можно определить в головном pom-файле проекта. Теперь накатывать изменения на обе БД можно одной командой:
mvn process-resources либо mvn clean install
Чтобы сделать rollback обеих баз на несколько changeset-ов назад к команде следует добавить параметры:
-Dliquibase.goal=rollback -Dliquibase.rollbackCount=1
liquibase.rollbackCount - определят кол-во changeset-ов которые следует откатить.
Порой может возникнуть ситуация, когда требуется внести изменение в существующий changeset и заново накатить все changeset-и. Здесь поможет параметр:
-Dliquibase.dropFirst=true
Если потребуется работать с каждой БД отдельно, то необходимо использовать уже команды самого плагина liquibase:
mvn liquibase:update либо mvn liquibase:rollback
Для тестовой БД добавляем параметр -Ptestdb в командной строке.