Большая часть приложений, которые мне встречались, хранят данные в SQL базе данных. Если у вас корпоративное приложение, то скорее всего имеется несколько стендов: стенд разработки, пре-прод и прод. А над приложением трудится команда разработчиков.
Такие приложения сталкиваются с проблемой синхронизации схемы БД между контурами и самими разработчиками. Надо как-то передать изменения, которые вы внесли всем остальным и при этом не получить конфликты.
Эти проблемы решает система управления миграциями Liquibase. Это своего рода система контроля версий вашей базы данных.
Liquibase - независимая от базы данных библиотека для отслеживания, управления и применения изменений схемы базы данных.
Изменения для БД записываются в формате понятном Liquibase, а уже он в свою очередь выполняет запросы к базе данных. Таким образом реализуется независимость от конкретной БД. Liquibase поддерживает 10 типов баз данных, включая DB2, Apache Derby, MySQL, PostgreSQL, Oracle, Microsoft® SQL Server, Sybase и HSQL. Список всех поддерживаемых БД можно посмотреть на сайте.
Существует другие системы управления миграциями: Doctrine 2 migrations, Rails AR migrations, DBDeploy и т.д. Но некоторые из них платформо-зависимые, некоторые не обладают таким широким функционалом.
Также серьезный недостаток многих систем — невозможность применения некоторых изменений без потери данных, например, переименование столбца произойдет как две операции: drop + add, что приведет к потере данных.
Как работает Liquibase
Liquibase — кросс платформенное Java приложение, это значит, что вы можете скачать JAR файл и использовать его на Windows, Mac или Linux.
Для примера мы будем рассматривать работу со spring-boot приложением и PostgresSQL базой данных. Но вы должны знать, что liquibase можно использовать и отдельно в виде .jar файла. Вот так:
java -jar liquibase.jar --driver=com.mysql.jdbc.Driver--classpath=lib/mysql-connector-java-5.1.21-bin.jar --changeLogFile=/path/to/changelog.yaml --url="jdbc:mysql://localhost/application" --username=dbuser --password=secret update
Changelog
Изменения структуры базы данных записываются в файлы, которые называются changelog. Поддерживаемые форматы: XML, YAML, JSON или SQL.
Файлы изменений могут быть произвольно включены друг в друга для лучшего управления. Подробнее об этом ниже.
Я являюсь ярым противником XML конфигураций, но в данном случае это самый удобный формат для записи миграций.
ChangeSet
ChangeSet – это аналог коммита в системах контроля версий, таких как Git. ChangeSet может содержать одно или несколько изменений базы данных. Хорошей практикой считается одна команда для одного ChangeSet.
Каждый changeSet имеет составной идентификатор id
, author
и filename
, который должен быть уникальным.
При первом запуске Liquibase создает две технические таблицы:
databasechangelog
– Содержит список изменений схемы БД. Туда записываются уже выполненные changeSet.databasechangelock
– Используется для блокировки на время работы, чтобы гарантировать одновременную работу только одного экземпляра Liquibase.
Блокировка
Если несколько экземпляров Liquibase будут выполняться одновременно с одной и той же базой данных, вы получите конфликты. Это может произойти, если несколько разработчиков используют один и тот же экземпляр базы данных или если в кластере несколько серверов, которые автоматически запускают Liquibase при запуске.
Для защиты от таких ситуаций Liquibase создает таблицу databasechangelock
, в которой есть boolean поле locked
. При запуске Liquibase проверяет его состояние, и если оно true
, то ожидает смены на false
.
Экстренно остановив выполнение программы в самом начале, может сложиться ситуация при котором Liquibase успеет поставить флаг, но не поменяет его на false
. В логах это будет выглядеть так:
Чтобы исправить эту проблему, в таблице databasechangelock
измените поле locked
на false
.
Контрольная сумма
Далее Liquibase читает главный changelog, проверяя какие изменения уже были приняты, а какие надо выполнить.
После выполнения changeSet в таблицу databasechangelog
со всем прочим записывается MD5 хэш changeSet. Хэш высчитывается на основе нормализованного содержимого XML.
При следующем запуске Liquibase будет сверять вновь рассчитанные хэш суммы, со значениями в его таблице. Если вы изменили уже выполненный changeSet, то хэш сумма будет отличаться, и приложение упадет с ошибкой при старте.
После выполнения changeset нельзя изменить
Начало работы
И так у нас уже есть spring-boot приложение, в которое мы хотим добавить Liquibase.
Репозиторий с примерами из статьи на GitHub:
https://github.com/Example-uPagge/liqubase
Настройка для spring-boot
Чтобы добавить поддержку Liquibase, нужно указать следующие зависимости в maven:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Так же в файл application.yml
укажем соединение с базой данных:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/liquibase_example
username: postgres
driver-class-name: org.postgresql.Driver
password: password
Если вы используете Hibernate, то не забудьте отключить создание схемы БД.
Теперь нам необходимо создать главный changelog. По умолчанию в spring-boot Liquibase ищет его в папке resources/db/changelog/db.changelog-master.yml
. Как я уже говорил мы будем использовать XML формат.
Создаем файл resources/db/changelog/db.changelog-master.xml
. И изменяем путь в application.yml
:
spring:
# .. .. .. .. ..
liquibase:
change-log: classpath:db/changelog/db.changelog-master.xml
Вставляем начальное содержимое в файл:
<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">
// сюда пишутся changeSets
</databaseChangeLog>
Чтобы быстро получить результат, мы создадим changeSet прямо в этом файле, а потом я расскажу почему так делать не стоит.
Создание таблицы
Создадим таблицу Person.
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="telegram_id" type="int">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
Тег createTable
содержит параметр tableName
, который указывает имя новой таблицы. Внутри этого тега мы перечислили колонки, которые нам нужны.
Для колонок обязательно необходимо указать тип. Тип указывается в формате Liquibase, после чего он приводится для конкретной реализации БД.
Отдельного внимания заслуживает колонка id. Для нее мы задали автоинкремент, а так же в constraints
указали ограничения колонки:
primaryKey="true"
– колонка является первичным ключом таблицы.nullable="false"
– значения не могут быть NULL.При использовании primaryKey параметр nullable не обязателен. Но если вы используете H2 для тестов, то у вас могут возникнуть проблемы из-за его отсутствия.
После запуска spring-boot приложения у нас будет создано 3 таблицы, одна из которых и будет person
.
Добавление колонки в таблицу
А теперь попробуем добавить новую колонку в таблицу в этом changeSet. Изменим его:
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="telegram_id" type="int">
<constraints unique="true"/>
</column>
<column name="address" type="varchar(300)"/>
</createTable>
</changeSet>
Снова запустив приложение мы получим ошибку.
Если changeSet уже выполнился, и запись об этом есть в databasechangelog
, то вы не можете просто изменить changeSet. Вы же не можете в git изменить уже опубликованный коммит.
В этом случае у вас три пути:
Создать новый changeSet с изменениями. [Рекомендуемый]
Выполнить откат средствами Liquibase.
Удалить запись о выполнении changeSet из
databasechangelog
. Не рекомендую этот вариант, если changeSet уже был выполнен на каком-то контуре. Этот вариант удобен для локальной разработке.
Вернем changeSet в его предыдущее состояние и создадим новый:
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="telegram_id" type="int">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
<changeSet id="add-new-column-address" author="uPagge">
<addColumn tableName="person">
<column name="address" type="varchar(300)"/>
</addColumn>
</changeSet>
Запускаем приложение. На этот раз успешно, новая колонка добавилась.
Связь с другой таблицей
Связь между таблицами довольно частое явление. Добавим новую таблицу Book
и свяжем ее с таблицей Person
. Создадим новый changeSet:
<changeSet id="create-table-book" author="uPagge">
<createTable tableName="book">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="author_id" type="int">
<constraints foreignKeyName="book_author_id_person_id" references="person(id)"/>
</column>
</createTable>
</changeSet>
Теперь атрибут author_id
связан с атрибутом id
в таблице Person
.
При этом обязательно нужно указать уникальный foreignKeyName
. Я пользуюсь следующим правилом: имя_таблицы + имя_поля + имя_главной_таблицы + имя_поля_главной_таблицы.
Также мы можем указать тип каскадной операции:
<constraints foreignKeyName="book_author_id_person_id" references="person(id)" deleteCascade="true"/>
Теперь, если автор книги будет удален, то книга тоже будет удалена.
Если вам необходима операция каскадного обновления, то вам нужен второй способ связи с таблицей:
<changeSet id="create-table-book" author="uPagge">
<createTable tableName="book">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="author_id" type="int"/>
</createTable>
<addForeignKeyConstraint baseTableName="book" baseColumnNames="author_id"
constraintName="book_author_id_person_id"
referencedTableName="person"
referencedColumnNames="id"
onUpdate="CASCADE"/>
</changeSet>
Создание представления
Несмотря на то, что к этому моменту вы уже полюбили создание изменений с помощью XML, для создания представления придется использовать SQL:
<changeSet id="create-view-book-author" author="uPagge">
<createView viewName="author_and_book">
SELECT p.id as person_id,
p.name as person_name,
b.id as book_id,
b.name as book_name
FROM person p
LEFT JOIN book b on p.id = b.author_id
</createView>
</changeSet>
Советы от бывалых
Познакомился я с Liquibase на своей стажировке в 2017. С тех пор я использую Liquibase на своих домашних проектах, и продвигаю его использование на рабочих.
Мне уже проще написать changeSet, чем SQL. Поэтому далее будет небольшой список рекомендаций, которые облегчат вам жизнь.
Организация ChangeSet
Добавляя все changeSet в один changelog у вас могло закрасться сомнение: а все ли мы правильно делаем. Схема БД может довольно динамично меняться, особенно в начале создания приложения, поэтому мы ожидаем множество changeSet.
Можно создавать множество ChangeLog и включать их друг в друга. Далее я расскажу о своем подходе к организации changelog.
Я придерживаюсь следующего подхода:
Для каждой текущей версии приложения создаем папку в
db/changelog
. Так как у нас еще нет версии приложения, то будем использовать папкуv.1.0.0
.В этой папке у нас будет локальный главный чейджлог-файл. Я называю их
cumulative.xml
.Когда вам необходимо внести набор изменений для схемы БД, то вы создаете отдельный changelog и включаете его в
cumulative.xml
.В
db.changelog-master.xml
мы подключаем всеcumulative.xml
.
Во время выпуска релиза у вас могут оказаться запросы на слияния, которые затрагивают добавления новых changeSet. В этих ПР необходимо создать новую папку для новых changeLogs с номером нового релиза и перенести туда changeLogs для этих ПРов.
Правила именования
Правило именования файлов позволяет без просмотра кумулятивных чейнджлогов файлов понять, что за чем следовало, и не допустит случайного повторения id
у changeSet.
Вы можете придумать свои правила, но вот что предлагаю я:
Каждый changelog, кроме
cumulative.xml
, начинается с текущей даты, а далее короткое описание всех изменений. Например:2020-03-08-create-tables.xml
Так же поступайте с
id
у changeSet. Напримерid="2020-03-08-create-table-person"
.
Не изменяйте данные
Работа с данными в БД не входит в число ключевых фич Liquibase и ограничивается лишь простейшими операциями вставки и удаления или изменения. Исходя из своего опыта крайне не рекомендую изменять данные с помощью Liquibase.
Кто-нибудь обязательно ошибется и ошибка уедет на тестовую среду, а откатывать придется вручную.
Идентификаторы к записям чаще всего генерируются автоматически, что может привести к дополнительным конфликтам.
Используйте XML
Иногда хочется «облегчить» жизнь и отказаться от XML, начав использовать более краткий DSL: groovy, yaml, json.
Все это очень хорошо до тех пор, пока вам не захочется иметь:
Авто дополнение в IDE
Автоматическую проверку формальной верности документа по схеме данных
Используйте remark
Разработчики стараются давать понятные имена для переменных, но дополнительное описание не будет лишним. Параметр remark
позволяет добавлять описания к таблицам и полям.
<changeSet id="2021-02-22-create-person" author="uPagge">
<createTable tableName="person" remarks="Пользователи системы">
<column name="id" type="int" autoIncrement="true" remarks="Идентификатор">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)" remarks="Имя пользователя">
<constraints nullable="false"/>
</column>
<column name="telegram" type="int" remarks="Идентификатор в телеграмм">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
Итого
Эта же статья в момем блоге: Версионирование структуры БД при помощи Liquibase.
В этой статье мы обсудили логику работы Liquibase, а так же простое внедрение его в ваше приложение. Этого уже достаточно, чтобы начать пользоваться Liquibase.
В следующей статье я расскажу, как откатывать изменения.
jreznot
А ещё для IntelliJ недавно появился плагин, который умеет Liquibase изменения сам генерировать по моделькам plugins.jetbrains.com/plugin/15075-jpa-buddy
andreyoganesyan
Да, чуть больше про него можно почитать тут: habr.com/ru/company/haulmont/blog/547514
А генерация Liquibase изменений выглядит так:
upagge Автор
Спасибо тебе добрый человек, а то лень было смотреть :D
upagge Автор
Прикольно конечно, но "кодогенерация" дело спорное
aleksey-stukalov
ну тогда блокнот… или netbeans :)…
Ручное написание бойлерплейта тоже дело спорное. Вопрос всегда в качестве :).
upagge Автор
Какой блокнот? idea и xml православный с автодополнением, и все.
Не знаю вообщем, никогда не было желания какой-то плагин добавить.
aleksey-stukalov
По xsd дополнения есть, а по модели? Таблички, поля? Да и вообще зачем писать это вручную, когда создание скрипта по изменениям более чем автоматизируется… В целом кому как удобно :)
upagge Автор
Да не, я просто пытаюсь понять профиты. Надо получше почитать посмотреть про этот плагин, а то так судить не правильно сразу по первому взгляду. Просто на моей практике кодогенерация никогда ничем хорошим не заканчивалась :D
aleksey-stukalov
Соглашусь с вами. Профит простой — вы автоматом генерите то, что приходится писать руками. Ну и всякие плюшки вроде дополниетльных испекций, кодгенов и автодополнений по коду.
upagge Автор
Ладно я потыкался минут 15 в плагин и вроде проникся, попробую использовать :)
aleksey-stukalov
Добавляйтесь в дискорд. Если будут вопросы по плагину с радостью там ответим. Идеи тоже most welcome :)
voopr
Немного не так. Уникальный идентификатор состоит из трех полей: id,author и filename. И если в последствии переместить файл в другой каталог, то liquibase воспримет уже примененный changeset как новый. Что бы этого избежать у databaseChangeLog можно задать атрибут logicalFilePath который и будет записан в filename.
aleksey-stukalov
Все верно, changeSet имеет составной ключ.
upagge Автор
Да, вы правы, еще filename. Поправлю статью, спасибо)
anonymous
И все таки непонятно зачем оно надо: мало к тому, что нужно быть БДшнкиом и знать sql, так к тому же еще и знать еще доп приблуду, и писать на ее языке планы изменений.
А нет ли приблуды, чтобы автоматом на основе разнесенных по времени подключений к бд делала диффы и превращала их в sql запросы? ну хоть для одной MySQL.
aleksey-stukalov
Сейчас занимаемся ровно такой задачей в рамках JPA Buddy, который упоминался выше по коментам. Будет работать для всех популярных реляционных баз.
upagge Автор
Не успел похвалить вашу приблуду, опередили :D
aleksey-stukalov
:)
upagge Автор
Насколько я пока понял в первом комментарии как раз идет речь о такой приблуде.
А накой оно надо, тут все предельно понятно. Есть 10 разработчиков, каждый что-то может менять в бд, особенно в самом начале жизни приложения. Вот чтобы каждый не вносил изменения других руками, и упорядочить все это дело оно и нужно. Ну и та же история с контурами пре-прода и прода, чтобы изменения схемы туда попадали автоматически. Запускаешь приложуху и все накатывается и везде все одинаково, красота.
Для особо классных парней она еще позволяет откатывать схему БД в обратную сторону. Грубо говоря была у вас классная версия приложения 1.9.1, вы набахали кучу изменений и перешли на версию 2.0.0, но через недельку поняли что что-то не так, а у вас уже 2.1 версия, вот такие вот вы молодцы. Пересобрать приложение 1.9.1 обычно не проблема, но могло что-то измениться в схеме БД. Вот liquibase позволит откатить и схему до версии 1.9.1
upagge Автор
В разработке в целом надо очень много знать, одной приблудой большо, одной меньше :D
anonymous
КАк показывает опыт — уровень знаний приблуд у свежих программеров стремиться к нулю и все падает на плечи няньки-девупса. а оно ему надо, лишняя суЧность, если это не автомат???!!!
upagge Автор
Ну попробуйте разрабатывать большие проекты без этой приблуды, потом возращайтесь, статья никуда не денется))
Опрос показывает, что приблуда нужная
aleksey-stukalov
Аххаха! А теперь вы прям на 1 сек опередили :D. 1 в 1 хотел ответить :).
whorules
Не совсем понятно, зачем использовать changeSet для написания sql запросов, если можно просто создать .sql файл и прописать к нему путь? Например
В строке path указывается путь до sql файла, в котором на классическом и удобочитаемом SQL написан скрипт
Классический sql знает чуть более, чем каждый бэкенд разработчик, а учить, и тем более использовать какой-то специфичный синтаксис, ещё и на xml, на мой взгляд, сомнительная идея.
В остальном, liquibase, бесспорно, must have в командной работе
vlad4kr7
В итоге у вас получается flyway. И зачем тогда liquibase?
Основной причиной почему liquibase НЕ надо использовать, это невозможно гарантировать какой именно SQL будет сгенерирован и выполнен. Dryrun конечно поможет но сравнить сгенерированное например с продом и пре продом будет тяжело. Надеюсь все понимают что код надо review and approve, и желательно DBA.
Да, кстати liquibase уже view, индексы и FK научился делать?
upagge Автор
Индексы научился и FK умеет, view пока только через выполнение SQL. Собственно пример есть в статье, кроме индексов.
bookmist
Как сторонник именно подхода Liquibase+sql отвечу. Более прозрачный порядок выполнения скриптов и возможность произвольной структуры папок и имен файлов. Возможность задать кодировку файлов. (Возможно flyway это умеет) Возможность условного выполнения скриптов.
Кроме того (но это уже не sql) возможность загружать в базу справочники из csv.
upagge Автор
А чего из этого нет в xml? Что такое более прозрачный? Мутные какие-то плюсы
bookmist
В xml это все как раз есть, во Flyway нет. Поэтому из liquibase+sql flyway никак не получается. На всякий случай я использовал xml, но скрипты писал на sql отдельными файлами. Использовать только SQL считаю неудобным по причинам, описанным в статье.
upagge Автор
А понял, я подумал что в Liquibase+sql это есть, а в Liquibase+xml этого нет :D
vlad4kr7
Liquibase это декларативный подход к описанию схемы, когда генерация и запуск доверяется программе. Очень полезная тулза для начальной стадии разработки. Flyway это контроль выполнения скриптов.
upagge Автор
В вашем примере тоже changeSet, возможно вы говорили про способ описания в формате liquibase.
Это лишает вас кросплотформенности БД. Например если по какой-то причине надо будет сьехать с одной БД на другую, то у некоторых БД немного свой синтаксис. Liquibase же сгенерирует SQL под конкретный формат БД.
Про переезд это довольно "фантастичный сценарий". Но вот использование на проде какого-нибудь Postgres, а для тестов H2 довольно частый кейс.
Когда я притащил в новый проект liquibase все разобрались в нем за 1-2 дня. Работа у нас такая, разбираться во всяких штуках, которые могут облегчить разработку))
Как раз xml и помогает в быстром освоении, о чем я написал в статье.
Используем вообщем XML и горя не знаем))
anpogrebnyak
Очень странный механизм блокировки БД для установки новой версии. Если Кубер убьет ваш сервис в момент заблокированной БД, то придется лезть в нее ручками и удалять блокировку. А особоенно неприятно, когда все это происходит в случае, когда вы ничего в своей БД не меняли, а просто при старте сервиса, Кубернетес его убил неважно по какой причине. И все это, естественно на проде, в субботу, ночью и т.д. и т.п.
upagge Автор
Да вы правы, но от части. Я знаю проекты, которые отлично живут с кубером и liquibase, все эти проблемы решаемы. Можно например накатывать схему пайплайном в гитлабе. В кубере есть такая штука как init контейнеры, можно через них бд накатывать.
anpogrebnyak
Таким образом вы лишаете себя еще одной фичи — при старте микросервис «сверяет» версию БД с той для которой он разрабатывался. Для вашего решения это неприменимо.
Мы эту проблему разрешили по-другому.
aleksandy
Да, но нет. Менять можно, и это даже не будет приводить к падениям, если правильно настроить.
xdenser
А у нас свой персистенс фреймворк, который строит модельку по аннотациям в классах и генерит скрипты для апдейта схемы БД. Новые поля и таблички апдейтсятся автоматом. От девелопера ничего не требуется кроме декларации новых дата моделей. т.е. версионность БД идет от кода. Перед стартом приложение строит модельку из БД и из класов — сравнивает. Если надо апдейт, то оно или обновляет автоматом (если это разрешено настройками) или запускается без БД и пускает только админа в экслюзивном режиме, чтобы он запустил обновление. Для кубера есть еще различие — требуется ли только что-то добавить или еще и удалить. Если только добавить — то проходит автоматом. Если удалить/переименовать то нужно окно для обслуживание БД — т.е. систему надо погасить и запустить в режиме обновления. Перименовка конечно автоматом не работает надо дописать скрипт для переноса данных. Ну и скрипт там навечно. Это позвовлет делать апгрейд с любой версии на большую автоматом. Откат версии конечно возможен в тяжелых случаях только с восстановлением из бекапа. Да и поддерживает Sybase, SQL, Oracle, Postgre. Foreign keys, индексы, софт. ссылки. Еще в коде поддерживает транзакции через аннотации и вручную. Фреймфорк существует еще с начала 2000х. В начале модели данных были отдельно от кода. Ну и поменьше движков поддерживалось. Но это не реклама. Потому что не продается отдельно.
kacetal
upagge Статья интересная спасибо. Но хотелось бы информации о том как liquibase с помошью мавен плагина может генерировать изменения на основе разницы между jpa сущностями и существующей базы. А то есть подозрение что там все сломано и оно не понимает новые конструкции языка.
upagge Автор
На самом деле я не использую такую фичу, но тема интересная, возможно разберусь и накатаю статейку
aleksey-stukalov
Для этого можно использовтать liquibase-hibernate plugin. Но ваше подозрение верное, там есть ряд ограничений, которые не дают нормально работать. Например, местами Liquibase выбирает странные типы колонок. Еще фактически вам нельзя будет использовать Hibernate Type-ы и JPA Converter-ы. Этот инструмент не понимает на какой тип надо маппить, т.к. не читает эти самые конвертеры. Плюс он подключается как зависимость, которая в проде не нужна...
В том числе для решения описанной вами задачи мы делали фичу в JPA Buddy — плагин для IntelliJ IDEA. Там мы учли и переопределение маппинга типов и научили правильно обрабатывать Hibernate Type-ы c JPA Converter-ами. В JPA Buddy вы можете выбрать что с чем сравнивать для создания diff changelog, в том числе использовать jpa сущности как источник информации о модели.