8. Использование событий сущности

На этом этапе вы завершили разработку модели данных и пользовательского интерфейса приложения. Но в логике приложения есть недостаток: сотрудники могут видеть и выполнять свои шаги по онбордингу на экране My onboarding, но атрибут Onboarding status сущности User не обновляется соответствующим образом.

В этой главе вы реализуете недостающую часть: атрибут Onboarding status сущности User будет обновляться всякий раз, когда изменяется состояние соответствующих экземпляров UserStep.

Создание слушателя EntityChangedEvent

Если ваше приложение запущено, остановите его с помощью кнопки Stop (suspend) на главной панели инструментов.

В окне инструментов Jmix нажмите New (add) → Event Listener:

listener 1

На первом шаге мастера Subscribe to Event выберите Entity Event:

listener 2

Нажмите кнопку Next.

На следующем шаге мастера выберите UserStep в поле Entity и установите флажок Entity Changed (before commit):

listener 3

Нажмите на кнопку Create.

Studio создаст Spring бин с помощью метода, аннотированного @EventListener:

@Component
public class UserStepEventListener {

    @EventListener
    public void onUserStepChangedBeforeCommit(EntityChangedEvent<UserStep> event) {

    }
}

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

Метод принимает объект EntityChangedEvent, который содержит идентификатор измененной сущности, тип изменения (создание/обновление/удаление) и информацию об измененных атрибутах.

Реализуйте слушатель, как показано ниже:

package com.company.onboarding.listener;

import com.company.onboarding.entity.OnboardingStatus;
import com.company.onboarding.entity.User;
import com.company.onboarding.entity.UserStep;
import io.jmix.core.DataManager;
import io.jmix.core.Id;
import io.jmix.core.event.EntityChangedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class UserStepEventListener {

    @Autowired
    private DataManager dataManager;

    @EventListener
    public void onUserStepChangedBeforeCommit(EntityChangedEvent<UserStep> event) {
        User user;
        if (event.getType() != EntityChangedEvent.Type.DELETED) {
            Id<UserStep> userStepId = event.getEntityId(); (1)
            UserStep userStep = dataManager.load(userStepId).one();
            user = userStep.getUser();
        } else {
            Id<User> userId = event.getChanges().getOldReferenceId("user"); (2)
            if (userId == null) {
                throw new IllegalStateException("Cannot get User from deleted UserStep");
            }
            user = dataManager.load(userId).one();
        }

        long completedCount = user.getSteps().stream()
                .filter(us -> us.getCompletedDate() != null)
                .count();
        if (completedCount == 0) {
            user.setOnboardingStatus(OnboardingStatus.NOT_STARTED); (3)
        } else if (completedCount == user.getSteps().size()) {
            user.setOnboardingStatus(OnboardingStatus.COMPLETED);
        } else {
            user.setOnboardingStatus(OnboardingStatus.IN_PROGRESS);
        }

        dataManager.save(user); (4)
    }
}
1 Если экземпляр UserStep был создан или обновлен, получить его идентификатор с помощью метода getEntityId() события. Затем загрузить экземпляр и получить связанный экземпляр User.
2 Если UserStep был удален, он больше не может быть загружен из базы данных. Но в этом случае event.getChanges() предоставляет значения всех атрибутов удаленного объекта.
3 Установить для атрибута onboardingStatus связанного пользователя значение, зависящее от состояния всех его элементов UserStep.
4 Сохранить обновленный экземпляр User в базе данных.

При наличии этого слушателя согласованность между коллекцией экземпляров UserStep и атрибутом onboardingStatus сущности User будет поддерживаться независимо от того, какой процесс изменяет экземпляры UserStep. Например, вы можете изменить UserStep непосредственно через Data ToolsEntity Inspector и по-прежнему видеть соответствующее изменение User.onboardingStatus.

Вы можете полагаться на слушателей событий EntityChangedEvent при работе с данными через DataManager. Если вы сохраняете изменения с помощью EntityManager или JDBC, слушатели не вызываются.

Резюме

Слушатели EntityChangedEvent могут использоваться для поддержания согласованности данных и выполнения бизнес-логики в текущей транзакции или после ее завершения. Слушатели EntityChangedEvent могут использоваться для поддержания согласованности данных и выполнения бизнес-логики в текущей транзакции или после ее завершения.