Hibernate - 拥有实体实例不再引用具有cascade =“all-delete-orphan”的集合

问题描述 投票:179回答:20

我在尝试更新我的实体时遇到以下问题:

"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 <..>,根据这个:How to "possible" solve the problem但它没有用。

如果您有任何想法,请告诉我。

谢谢!

java hibernate hibernate-mapping
20个回答
180
投票

检查您为sonEntities分配内容的所有地方。您引用的链接明确指出创建一个新的HashSet,但您可以在重新分配该集时随时出现此错误。例如:

public void setChildren(Set<SonEntity> aSet)
{
    this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}

通常,您只想在构造函数中“设置”一次“新”。每当您想要添加或删除列表中的内容时,您必须修改列表的内容而不是分配新列表。

要添加孩子:

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}

要删除孩子:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}

1
投票
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>()

当我将子对象添加到现有的子对象列表时,我遇到了同样的错误。

childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);

解决我的问题是:child = childService.saveOrUpdate(child);

现在,孩子也恢复了其他细节,并且工作正常。


0
投票

加上我愚蠢的答案。我们正在使用Spring Data Rest。这是我们非常标准的关系。该模式在别处使用。

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()


//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent

通过我们创建的关系,始终打算通过他们自己的回购添加孩子。我还没有添加回购。我们进行的集成测试是通过REST调用完成实体的完整生命周期,因此事务将在请求之间关闭。没有孩子的回购意味着json将孩子作为主要结构的一部分而不是_embedded。对父母的更新会导致问题。


0
投票

以下解决方案为我工作

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()

//Updated setter of children 
public void setChildren(List<Children> children) {
    this.children.addAll(children);
    for (Children child: children)
        child.setParent(this);
}


//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;

0
投票

而不是分配新的集合

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

替换所有元素

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}

0
投票

另一个原因可能是使用lombok。

@Builder - 即使你说Collections.emptyList(),也能保存.myCollection(new ArrayList());

@Singular - 忽略类级默认值并离开字段null,即使类字段被声明为myCollection = new ArrayList()

我的2美分,只花了2个小时与相同:)


0
投票

当我设置A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance时,我得到了parent.setChildren(new ArrayList<>())。当我改为parent.getChildren().clear()时,它解决了这个问题。

查看更多详情:HibernateException - A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance


0
投票

小心

BeanUtils.copyProperties(newInsum, insumOld,"code");

这种方法打破了休眠。


0
投票

我正在使用Spring Boot,虽然没有直接覆盖它,但是我还是将这个问题与一个集合有关,因为我使用custom serializer and deserializer为同一个集合声明了一个额外的字段,以便提供更加前端友好的数据表示:

  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);
  }

0
投票

它可能是由hibernate-enhance-maven-plugin引起的。当我启用enableLazyInitialization属性时,这个异常开始在我的懒惰集合上发生。我正在使用hibernate 5.2.17.Final。

注意这两个休眠问题:


0
投票

我和Spring Boot完全不同!对我而言,这不是因为设置了收集属性。

在我的测试中,我试图创建一个实体,并为另一个未使用的集合收到此错误!

经过这么多的尝试,我只是在测试方法上添加了一个@Transactional并解决了它。不过没有原因。


87
投票

方法:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}

如果parentEntity分离,则工作,如果我们更新它,则再次工作。 但是如果实体没有从每个上下文中分离出来(即查找和更新操作在同一个事务中),则以下方法有效。

public void setChildren(Set<SonEntity> aSet) {
    //this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
    this.sonEntities.clear();
    if (aSet != null) {
        this.sonEntities.addAll(aSet);
    }
}

0
投票

我在使用JSON post请求更新实体时遇到了这个问题。当我更新实体而没有关于子项的数据时,即使没有子项,也会发生错误。添加

"children": [],

到请求机构解决了问题。


22
投票

当我在各种地方读到hibernate不喜欢你分配给一个集合时,我认为最安全的做法显然是让它最终像这样:

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

但是,这不起作用,并且你得到了可怕的“不再引用”错误,这在这种情况下实际上是非常误导的。

事实证明,hibernate调用你的setRoles方法并且它希望在这里安装它的特殊集合类,并且不接受你的集合类。尽管阅读了有关未按照set方法分配到集合的所有警告,但这让我难以忍受了很久。

所以我改为:

public class User {
  private Set<Role> roles = null;

  public void setRoles(Set<Role> roles) {
  if (this.roles == null) {
    this.roles = roles;
  } else {
    this.roles.retainAll(roles);
   this.roles.addAll(roles);
  }
}
}

因此,在第一次调用时,hibernate将安装其特殊类,并且在后续调用中,您可以自己使用该方法而不会破坏所有内容。如果你想把你的类用作bean,你可能需要一个工作的setter,这至少似乎有效。


14
投票

实际上,我的问题是关于我的实体的equals和hashcode。遗留代码可能带来很多问题,永远不要忘记检查它。我所做的只是保持delete-orphan策略并纠正equals和hashcode。


9
投票

我有同样的错误。对我来说问题是,在保存实体之后,映射的集合仍然为null,并且在尝试更新实体时抛出了异常。什么对我有帮助:保存实体,然后进行刷新(集合不再为空),然后执行更新。也许使用新的ArrayList()或其他东西初始化集合也可能有所帮助。


4
投票

有关系类型:


当它在hasMany中声明时,不要尝试实例化该集合,只需添加和删除对象。

class Parent {
    static hasMany = [childs:Child]
}

使用关系类型:


但是,只有当声明为属性(使用关系)并且未在声明中初始化时,该集合才可以为null。

class Parent {
    List<Child> childs = []
}

3
投票

我试图使用TreeSet时遇到了这个问题。我确实用oneToMany初始化TreeSet

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();

但是,这将带来question上面描述的错误。因此,似乎hibernate支持SortedSet,如果只是改变上面的行

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;

它像魔术一样工作:)有关hibernate SortedSet的更多信息可以是here


3
投票

我使用@ 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);
            }
        }
    }
}

2
投票

我遇到此错误的唯一一次是当我尝试将NULL传递给集合的setter时。为了防止这种情况,我的setter看起来像这样:

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.