Журнал сущностей

Журнал сущностей - это механизм для отслеживания изменений, вносимых в JPA-сущности. Он фиксирует изменения атрибутов сущностей и предоставляет пользовательский интерфейс для запроса и отображения деталей изменений:

  • Какая сущность была изменена.

  • Старые и новые значения измененных атрибутов.

  • Когда сущность была изменена.

  • Какой пользователь изменил сущность.

Регистрация изменений

Журнал сущностей автоматически фиксирует изменения, вносимые в JPA-сущности при коммите транзакции при работе с DataManager или EntityManager. Он не функционирует при использовании нативного SQL.

Также, если вы загружаете сущности через EntityManager, вы можете использовать бин EntityLog напрямую для регистрации изменений в сущностях из кода вашего приложения. В этом случае вызывайте методы registerCreate(), registerModify() и registerDelete() с параметром auto, установленным в false. Когда журнал сущностей вызывается фреймворком автоматически, этот параметр устанавливается в true.

Настройка журнала сущностей

Для использования административного интерфейса во время выполнения пользователь должен иметь ресурсную роль entity-log, предоставляемую дополнением Audit.

Вы можете настроить журнал сущностей в экране Audit → Entity log. Перейдите на вкладку Setup:

entity log set

Чтобы создать конфигурацию журнала сущностей, нажмите кнопку Create.

  • Выберите сущность из выпадающего списка Name и выберите атрибуты для аудита.

  • Флажок Auto определяет, регистрирует ли система изменения, когда EntityLog вызывается подсистемой JPA при коммите транзакции (с параметром auto = true).

  • Флажок Manual определяет, регистрирует ли система изменения, когда EntityLog вызывается кодом приложения (с параметром auto = false).

  • Действия Export и Import позволяют экспортировать и импортировать конфигурации в формате JSON или ZIP.

Также вы можете настроить журнал сущностей, добавив записи в базу данных, если хотите включить конфигурацию в инициализацию базы данных.

Конфигурация логирования включает сущности LoggedEntity и LoggedAttribute, которые соответствуют таблицам AUDIT_LOGGED_ENTITY и AUDIT_LOGGED_ATTR.

  • LoggedEntity определяет типы сущностей, которые должны логироваться.

  • LoggedAttribute определяет атрибут сущности для логирования и содержит ссылку на LoggedEntity и имя атрибута.

Чтобы настроить логирование для конкретной сущности, вставьте соответствующие записи в таблицы AUDIT_LOGGED_ENTITY и AUDIT_LOGGED_ATTR.

В следующем примере настройка логирования изменений атрибута phone сущности Customer выполняется во время инициализации базы данных:

<changeSet id="1" author="audit">
    <insert tableName="AUDIT_LOGGED_ENTITY">
        <column name="ID" value="0a6ba81c-a8b9-bc8f-3829-53a6cef48871"/>
        <column name="CREATED_BY" value="admin"/>
        <column name="CREATE_TS" valueDate="2024-02-21T14:57:25.339"/>
        <column name="NAME" value="Customer"/>
        <column name="AUTO" value="true"/>
        <column name="MANUAL" value="true"/>
    </insert>
</changeSet>
<changeSet id="2" author="audit">
    <insert tableName="AUDIT_LOGGED_ATTR">
        <column name="ID" value="8e6e9825-1381-5299-e704-eadf1b96996e"/>
        <column name="CREATE_TS" valueDate="2024-02-21T14:57:25.339"/>
        <column name="CREATED_BY" value="admin"/>
        <column name="ENTITY_ID" value="0a6ba81c-a8b9-bc8f-3829-53a6cef48871"/>
        <column name="NAME" value="phone"/>
    </insert>
</changeSet>

Просмотр журнала сущностей

Для доступа к содержимому журнала сущностей перейдите на вкладку View в экране Audit → Entity Log. Настройте необходимые фильтры для поиска конкретных записей журнала.

entity log view

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

Записи журнала хранятся в таблице AUDIT_ENTITY_LOG, соответствующей сущности EntityLogItem. Значения измененных атрибутов хранятся в колонке CHANGES и конвертируются в экземпляры сущности EntityLogAttr.

В следующем примере экран деталей сущности Order отображает таблицы данных, содержащие информацию журнала сущностей. Ниже приведен фрагмент XML-дескриптора экрана:

<data>
    <instance id="orderDc"
              class="com.company.demo.entity.Order">
        <fetchPlan extends="_base"/>
        <loader/>
    </instance>
    <collection id="entityLogItemsDc"
                class="io.jmix.audit.entity.EntityLogItem"> (1)
        <fetchPlan extends="_local"/>
        <loader id="entityLogItemsDl" readOnly="true">
            <query>
                <![CDATA[select e from audit_EntityLog e
                where e.entityRef.entityId = :entityOrder]]>
            </query>
        </loader>
        <collection id="entityLogAttrDc" property="attributes"/> (2)
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
<actions>
    <action id="saveAction" type="detail_saveClose"/>
    <action id="closeAction" type="detail_close"/>
</actions>
<layout>
    <formLayout id="form" dataContainer="orderDc">
        <datePicker id="dateField" property="date"/>
        <textField id="productField" property="product"/>
        <textField id="amountField" property="amount"/>
        <textField id="priceField" property="price"/>
    </formLayout>
    <formLayout>
        <dataGrid id="entityLogItemsDataGrid"
                  dataContainer="entityLogItemsDc"> (3)
            <columns>
                <column property="eventTs"/>
                <column property="username"/>
                <column property="type"/>
            </columns>
        </dataGrid>
        <dataGrid id="entityLogAttrsDataGrid"
                  dataContainer="entityLogAttrDc"> (4)
            <columns>
                <column property="name"/>
                <column property="oldValue"/>
                <column property="value"/>
            </columns>
        </dataGrid>
    </formLayout>
    <hbox id="detailActions">
        <button id="saveAndCloseBtn" action="saveAction"/>
        <button id="closeBtn" action="closeAction"/>
    </hbox>
</layout>
1 Загрузка коллекции EntityLogItem в контейнер данных entityLogItemsDc.
2 Загрузка связанных экземпляров EntityLogAttr в контейнер данных entityLogAttrDc.
3 Привязка таблицы данных к контейнеру entityLogItemsDc.
4 Привязка таблицы данных к контейнеру entityLogAttrDc.

Контроллер экрана Order выглядит следующим образом:

@ViewComponent
private CollectionLoader<EntityLogItem> entityLogItemsDl;

@Subscribe(id = "orderDc", target = Target.DATA_CONTAINER)
public void onOrderDcItemChange(final InstanceContainer.ItemChangeEvent<Order> event) { (1)
    entityLogItemsDl.setParameter("entityOrder",event.getItem().getId());
    entityLogItemsDl.load();
}
1 В обработчике события ItemChangeEvent контейнера orderDc параметр устанавливается в зависимый загрузчик, и он запускается.