我有以下问题时,试图更新我的实体:

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".

我有一个父实体,它有一个Set<…>的子实体。当我尝试更新它时,我得到了要设置到这个集合的所有引用并设置它。

下面的代码表示我的映射:

@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
    return this.children;
}

我已经尝试清理Set<..只有>,根据这个:如何“可能”解决问题,但它没有工作。

如果你有什么想法,请告诉我。

谢谢!


当前回答

当我们以孩子为最终选择时,问题就解决了。

我们不应该在构造函数和setter中改变子对象的引用。

其他回答

我使用Spring Boot,有这个问题与一个集合,尽管没有直接覆盖它,因为我声明了一个额外的字段为同一个集合与自定义序列化和反序列化,以提供一个更前端友好的数据表示:

  public List<Attribute> getAttributes() {
    return attributes;
  }

  public void setAttributes(List<Attribute> attributes) {
    this.attributes = attributes;
  }

  @JsonSerialize(using = AttributeSerializer.class)
  public List<Attribute> getAttributesList() {
    return attributes;
  }

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes = attributes;
  }

似乎即使我自己没有覆盖集合,反序列化也会在底层执行,从而触发这个问题。解决方案是改变与反序列化器相关联的setter,这样它就会清除列表并添加所有内容,而不是覆盖它:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }

我使用@user2709454方法,但改进很小。

public class User {
    private Set<Role> roles;

    public void setRoles(Set<Role> roles) {
        if (this.roles == null) {
            this.roles = roles;
        } else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
            this.roles.clear();
            if(roles != null){
                this.roles.addAll(roles);
            }
        }
    }
}

所有这些答案都帮不了我,但我找到了另一个解决办法。

我有一个实体A包含一个实体B的列表实体B包含一个实体C的列表。

我试图更新实体A和b,它成功了。但是当更新实体C时,我得到了上述错误。在实体B中,我有一个这样的注释:

@OneToMany(mappedBy = "entity_b", cascade = [CascadeType.ALL] , orphanRemoval = true)
var c: List<EntityC>?,

我只是删除了orphanRemoval和更新工作。

在我的例子中,它是从几个线程并发访问一个Hibernate会话。 我有Spring Boot Batch和RepositoryItemReader实现,其中我通过大小为10的页面请求获取实体。

例如,我的实体是:

@Entity
class JobEntity {
    @ManyToOne(fetch = FetchType.LAZY)
    private GroupEntity group;
}

@Entity
class GroupEntity {
    @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    private Set<Config> configs; 
}

批处理:在一个事务中读取器->处理器->写入器。

在该实体配置中,GroupEntity可以转义到其他线程:

进入read部分的第一个线程获取大小为10的JobEntity页面(RepositoryItemReader#doRead),该项目包含一个共享的GroupEntity对象(因为它们都指向相同的组id)。然后取第一个实体。接下来读取部分的线程一个接一个地从该页中取出JobEntity,直到耗尽该页。

所以现在线程可以访问与JobEntity实例相同的GroupEntity实例,这是不安全的多线程访问一个Hibernate会话。

当我设置parent时,拥有的实体实例不再引用级联= " all-delete-orphan "的集合。setChildren(新ArrayList < >())。当我改为parent.getChildren().clear()时,它解决了这个问题。

请查看更多详细信息:HibernateException - cascade="all-delete-orphan"的集合不再被所属实体实例引用。