如何强制Hibernate在更新之前删除孤儿记录

5
假设我有以下模型结构:
@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的组,其中包含AnnotationOptionopt1opt2opt3选项。

现在我想用一个选项opt1替换所有选项。

enter image description here

此外,我在数据库中还有约束条件:

    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.

实际上,问题是Hibernate在更新后删除孤儿对象。

你能建议一些解决方法吗?


我有同样的问题。请问您是如何解决的? - J. Adam
@J. Adam 如果我没记错的话,问题在于我重新创建了一个集合(将新集合分配给选项字段)。正确的方法是使用旧集合(您可以使用options.clear()然后添加所需的元素)。 - gstackoverflow
2个回答

6

这个例子有很多问题:

  1. @OneToMany集合上的EAGER获取几乎总是不明智的
  2. 单向集合也不好,使用双向集合
  3. 如果你遇到了这个异常,很可能是你清除了所有元素,然后重新添加了想要保留的元素。

修复它的最佳方法是显式地将现有的子集与传入的子集合并,以便:

  1. 新的子实体被添加到集合中。
  2. 不再需要的子实体被删除。
  3. 匹配业务键(annotation_group_namestudy_id)的子实体使用传入的数据进行更新。

你的意思是在集合和设置ID上吗? - gstackoverflow
如果您阅读我在此答案中提供的链接文章,您将会理解为什么单向集合是不好的。 - Vlad Mihalcea

1
根据Hibernate文档,为了保留外键约束,Hibernate执行以下顺序:
  1. 按执行顺序插入
  2. 更新
  3. 删除集合元素
  4. 插入集合元素
  5. 按执行顺序删除
如果有特殊需要,您应该手动刷新事务以强制在数据库中进行删除。

我不进行显式的删除。我替换集合。 - gstackoverflow
如果你的意思是替换一个新的,请不要这样做,Hibernate使用自己实现的List。要将列表中所有元素标记为持久化上下文中的“待删除”状态,请使用List.clear()。 - Baptiste Beauvais

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接