OneToOne ассоциации и Soft Deleted сущности

Добрый день.

JMIX 1.7.1

Ниже описал ситуацию, с которой я столкнулся в реальном проекте.
Для ассоциации @OneToOne(mappedBy) в EntityChangedEvent в качестве oldValue загружается мягко удалённая сущность (удалённая не в текущей транзакции а когда-либо ранее).

Есть две сущности в которых нас интересует ассоциация между ними:

@JmixEntity
@Table(name = "SD_ASSIGNMENT")
@Entity(name = "sd_Assignment")
public class Assignment {
    @OneToOne(mappedBy = "assignment", fetch = FetchType.LAZY)
    private Step step;
}

@JmixEntity
@Table(name = "SD_STEP")
@Entity(name = "sd_Step")
public class Step {

    @DeletedBy
    @Column(name = "DELETED_BY")
    private String deletedBy;

    @Temporal(TemporalType.TIMESTAMP)
    @DeletedDate
    @Column(name = "DELETED_DATE")
    private Date deletedDate;

    @NotNull
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ASSIGNMENT_ID", nullable = false, updatable = false)
    private Assignment assignment;

}

Так же есть обработчик события:

@EventListener
public void onAssignmentChangedBeforeCommit(final EntityChangedEvent<Assignment> event) {
    final AttributeChanges changes = event.getChanges();
    System.out.println(changes.getAttributes());
}

Последовательность действий:

  1. Создать Assignment
  2. Создать Step (и привязать его к Assignment)
  3. Отредактировать name у Assignment и увидеть в обработчике (EntityChangedEvent<Assignment>) что изменился только name.
  4. Удалить Step.
  5. Создать новый Step и привязать его к тому же Assignment. Тут у нас получается два экземпляра Step, которые связаны с Assignemnt, но один из них мягко удалён.
  6. Отредактировать name у Assignment и увидеть в обработчике (EntityChangedEvent<Assignment>) что изменился НЕ только name, но и step. И если посмотреть дебагером в changes, то увидим что для step в oldValue будет удалённый step.

soft-deleted.zip (98.9 КБ)

Евгений, здравствуйте!

Спасибо, что обратили внимание на проблему и прикрепили пример для воспроизведения!
Ситуация действительно выглядит несогласованной, завел issue чтобы посмотреть, что с этим можно сделать.

Для обработки ссылочных атрибутов при мягком удалении сущности можно использовать аннотации @OnDelete и @OnDeleteInverse. В нашем случае можно решить проблему, добавив @OnDelete к атрибуту Step#assignment:

    @OnDelete(DeletePolicy.UNLINK)
    //@NotNull
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ASSIGNMENT_ID", updatable = false)
    private Assignment assignment;

Как альтернативный воркэраунд, можно добавить слушатель для обработки изменения поля assignment, обновляя, соответственно, и атрибут “Assignment#step”, чтобы его слушатель сработал сразу.

С уважением,
Дмитрий

День добрый.

Спасибо за ответ.

Да, @OnDelete(DeletePolicy.UNLINK) решает проблему с загрузкой удаленных ассоциированных сущностей, однако “потеря” ассоциации делает невозможным дальше восстановление удалённой сущности, ну или даже просто проведение какого-то анализ что с чем было связано, а это может оказаться нужным в бизнес-процессе в реальном приложении.

На данный момент я нашёл пару решений для себя, в том числе просто дополнительно использование колонки OneToOne на стороне assignment, которая хранит id актуального step.

Однако если всё таки это баг, надеюсь найдётся решение на уровне фреймворка.

Я пробовал написать MappingProcessor для настройки конкретно этой ассоциации, но там что-то другое перестало работать, уже не помню что.