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

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

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

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

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

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

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

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

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

EntityLog может регистрировать изменения только для managed (управляемых) сущностей внутри транзакции. Это означает, что вы должны вызывать EntityLog из кода приложения только если вы используете EntityManager для загрузки данных. Сущности, загруженные с помощью DataManager, всегда являются detached (открепленными), и их изменения не могут быть зарегистрированы путем вызова EntityLog вручную.

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

Для использования административного интерфейса во время выполнения пользователь должен иметь ресурсную роль 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 параметр устанавливается в зависимый загрузчик, и он запускается.