8. Использование событий сущности
На этом этапе вы завершили разработку модели данных и пользовательского интерфейса приложения. Но в логике приложения есть недостаток: сотрудники могут видеть и выполнять свои шаги по онбордингу на экране My onboarding, но атрибут Onboarding status сущности User не обновляется соответствующим образом.
В этой главе вы реализуете недостающую часть: атрибут Onboarding status сущности User будет обновляться всякий раз, когда изменяется состояние соответствующих экземпляров UserStep.
Создание слушателя EntityChangedEvent
Если ваше приложение запущено, остановите его с помощью кнопки Stop () на главной панели инструментов.
В окне инструментов Jmix нажмите New () → Event Listener:
 
На первом шаге мастера Subscribe to Event выберите Entity Event:
 
Нажмите кнопку Next.
На следующем шаге мастера выберите UserStep в поле Entity и установите флажок Entity Changed (before commit):
 
Нажмите на кнопку 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 Tools → Entity Inspector и по-прежнему видеть соответствующее изменение User.onboardingStatus.
| Вы можете полагаться на слушателей событий EntityChangedEventпри работе с данными черезDataManager. Если вы сохраняете изменения с помощьюEntityManagerили JDBC, слушатели не вызываются. | 
Резюме
Слушатели EntityChangedEvent могут использоваться для поддержания согласованности данных и выполнения бизнес-логики в текущей транзакции или после ее завершения. Слушатели EntityChangedEvent могут использоваться для поддержания согласованности данных и выполнения бизнес-логики в текущей транзакции или после ее завершения.