Как заставить Hibernate удалить сирот перед обновлением

Скажем, у меня есть следующая структура модели:

@Entity
@Table(....)
public class AnnotationGroup{
    ...
    private List<AnnotationOption> options;


    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name = "annotation_group_id", nullable = false)
    public List<AnnotationOption> getOptions() {
        return options;
    }
}

@Entity
@Table(...)
public class AnnotationOption {

    private Long id;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Override
    public Long getId() {
        return id;
    }
}

В данный момент у меня есть group1 с AnnotationOption opt1 opt2 и opt3

Затем я хочу заменить все опции только одним вариантом opt1

введите описание изображения здесь

Кроме того, у меня есть ограничение в базе данных:

    CONSTRAINT "UQ_ANNOTATION_OPTION_name_annotation_group_id" UNIQUE (annotation_option_name, annotation_group_id)

И этот огонь срабатывает:

Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "UQ_ANNOTATION_OPTION_name_annotation_group_id"
  Detail: Key (name, annotation_group_id)=(opt1, 3) already exists.

Фактически isuue, что hibernate удаляет сирот после обновления.

Можете ли вы предложить что-то решить проблему?

Ответ 1

В этом примере так много ошибок:

  • Получение EAGER в коллекции @OneToMany почти всегда плохая идея.
  • Однонаправленные коллекции также плохие, используйте двунаправленный.
  • Если вы получаете это исключение, скорее всего, вы очистили все элементы и снова добавили те, которые хотите сохранить.

Лучший способ исправить это - явно слить существующий набор дочерних элементов с входящими, чтобы:

  • Новые дочерние сущности добавляются в коллекцию.
  • Удаляются дочерние объекты, которые больше не нужны.
  • Дочерние объекты, соответствующие бизнес-ключу (annotation_group_name, study_id), обновляются с помощью входящих данных.

Подробнее см. Высокая производительность Java Persistence.

Ответ 2

В соответствии с Hibernate documentation hibernate выполняется в следующем порядке, чтобы сохранить ограничение внешнего ключа:

  • Вставляет, в порядке их выполнения.
  • Обновления
  • Удаление элементов коллекции
  • Вставка элементов коллекции
  • Удаляет, в порядке их выполнения.

Для вашей особой необходимости вам необходимо вручную очистить транзакцию, чтобы принудительно удалить ее в базе данных.