Запрашиваем историю изменений записи таблицы базы данных с помощью Hibernate Envers

Продолжаем изучать библиотеку Hibernate Envers. В этой статье мы рассмотрим получение информации из истории изменений записи.

Сразу отметим, что перед тем, как продолжить чтение этой статьи настоятельно рекомендуется ознакомиться с предыдущей статьёй, где мы разбирали основы работы с Hibernate Envers, в том числе подключение журналирования. В противном случае вам будет сложно понять, о чем пойдёт речь далее.
Получение данных из истории изменений с помощью AuditQuery

Сохранение истории изменений данных это даже в лучшем случае только половина дела. В случае необходимости нужно уметь получать из неё нужную информацию. Для этого Hibernate Envers предоставляет объект AuditQuery.

Для начала рассмотрим простейший пример – получение версии данных, соответствующих номеру редакции.

Здесь и далее мы будем использовать пример из предыдущей статьи.

Здесь и далее em – это объект EntitityManager, который в Spring или Spring Boot можно получить, внедрив соответствующую зависимость.

Теперь решим более сложную задачу. Получим всю историю изменений для записи целиком.

Здесь для создания запроса уже используется метод forRevisionsOfEntity, который формирует список редакций для записей таблицы. Этот метод имеет два важных параметра типа Boolean. Если первый из них (второй по счёту параметр метода) равен true, то запрос вернёт только набор самих данных без информации о редакции. Второй (последний параметр метода) определяет возвращать ли сведения об удалённых записях (если true данные возвращаются). Таким образом в примере выше запрос сконфигурирован таким образом чтобы получить наиболее полную информацию о тех изменениях, которые происходили с записью.

В целях видимо универсальности Hibernate Envers возвращает подобные данные из истории в формате List<Object[]>, что создаёт ощутимое неудобство при работе. Однако объекты в каждом массиве имеют чёткую структуру. Эта структура и правильный разбор таких данных будут рассмотрены ближе к концу статьи.

В завершение нашего знакомства с AudutQuery рассмотрим ещё более сложный вариант. А, именно получение данных из истории изменений по состоянию на указанную дату.

В этом примере мы добавили отбор по интервалу дат. Но тут есть одна тонкость.

Дело в том, что дата и время создания редакции хранится в БД в формате UNIX Timestamp в миллисекундах в виде целого числа (например, в PostgreSQL это поле имеет тип int8). Поэтому при работе с типами данных даты и времени в Java необходимо предварительно переводить их миллисекунды и только лишь после этого подставлять в запрос.

По этой же причине запрос делается в интервале дат (данные за 1 декабря будут между 0 ч. 0 мин. 1 декабря и 0 ч. 0 мин. 2 декабря).

Альтернативный способ получения данных из истории изменений

В случае нежелания работать с AuditQuery можно создать модель для соответствующих таблиц, которые создаёт Hibernate Envers и использовать для получения данных стандартные средства Hibernate. Однако при большом количестве моделей, изменения в которых необходимо отслеживать, такой подход будет слишком громоздким и только создаст неоправданные сложности и трудозатраты.

Также в очень старых версиях у объекта AuditReader был метод getRevisionForDate, который позволял получать сразу редакции на указанную дату, но на данный момент он уже давным-давно удалён разработчиками.

Интерпретация данных истории изменений

Как уже говорилось выше, формат List<Object[]>, который по умолчанию используется для данных истории изменений не удобен. Но, его можно легко интерпретировать в список объектов определённой структуры.

Эту структуру можно представить в виде следующих двух классов. Первый из них описывает весь набор данных для редакции целиком.

Второй описывает дополнительную информацию о редакции.

Справедливости ради для последней в Hibernate Envers есть стандартный класс DefaultRevisionEntity, который по своей сути аналогичен приведённому, но им лучше не пользоваться, т.к. вследствие особенностей работы Hibernate с его сериализацией в тот же JSON могут возникнуть сложности связанные с hibernateLazyInitializer.

Перевести List<Object []>, который возвращает Hibernate Envers, в List<RevisionData> можно следующим способом:

Интеграция получения данных из истории изменений со стандартным репозиторием модели

Получение данных из истории изменений удобно включит в репозиторий соответствующей сущности.

В нашем случае он представляет собой простейший JPA репозиторий.

Создадим интерфейс, в котором объявим нужные нам методы.

Затем добавим его реализацию. На ней мы останавливаться не станем, т.к. она уже достаточно детально описана ранее

Обратите внимание! Имя класса должно формироваться по правилу: имя_интерфейса+Impl. Иначе ничего работать не будет!

Далее остаётся только указать репозиторий в качестве наследника реализованного нами интерфейса.

Теперь мы можем получать данные из истории изменений посредством обычного репозитория.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *