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


225

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

"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 <..>:如何“可能”解决问题,但没有成功。

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

谢谢!


1
@ mel3kings,您提供的链接不再有效。
蛋白石


删除元素时尝试使用可变集合。例如,不要使用something.manyother.remove(other)if manyotherList<T>。使其他许多ArrayList<T>orphanDelete = true
变量变得

Answers:


220

检查所有要为sonEntities分配内容的地方。您引用的链接清楚地指出了创建一个新的HashSet的过程,但是在您重新分配该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);
}

8
实际上,我的问题是关于实体的等于和哈希码。遗留代码会带来很多问题,永远不要忘记对其进行检查。我所做的只是保持删除-孤立策略,并纠正equals和hashcode。
axcdnt 2011年

6
很高兴您解决了您的问题。equals和hashcode在Hibernate中使我难过几次。而不是用“ [已解决]”更新问题的标题,您应该继续并发布答案,然后将其标记为接受的答案。
brainimus 2011年

谢谢,我遇到了类似的东西变成了我的二传手该集是空的...
悉达多

是的,即使在空列表上,也会出现此错误。我不希望因为没有孤儿(列表为空)。
tibi

4
通常,您只想在构造函数中一次“新”集合。每当您想向列表中添加或删除某些内容时,都必须修改列表的内容,而不是分配新的列表。最高曝光率
Nikhil Sahu '18

109

方法:

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

1
@我应该遇到类似的问题,然后应用您的解决方案(在setter方法中,我清除了children集合-this.children.clear(),并添加了新的children-this.children.addAll(children))。此更改无法解决我的问题。我仍然收到“拥有实体实例不再引用具有collection =“ all-delete-orphan”的集合”异常。你知道为什么吗?非常感谢你!
ovdsrn

@ovdsrn抱歉,这不是我的答案,我只是整理出答案的格式,原始作者(kmmanu)可能会为您提供帮助(或者,如果您的情况与以下情况有所不同,则可能要提出一个新问题)原来的问题在这里问)祝你好运
Skuld

1
这很好用,但是当子级包含在嵌套的休眠组件中并且该组件在其Entity中被设置为null时,效果不是很好。然后,您将收到相同的错误。这是因为子级的所有者是根Entity,而不是被设置为null的组件...这样,一个组件永远不允许为null,而应导致转发给该对象中的destroy()方法。孩子...至少,我不知道有更好的解决方案...这是一种clear()构造的集合,但随后通过destroy()在一个组件上...
edbras 2014年

也看到这个帖子更多细节上面:stackoverflow.com/questions/4770262/...
edbras

7
请在sonEntities.clear()上使用this.sonEntities.retainAll(aSet),因为如果aSet == this.sonEntities(即同一对象),则在添加之前将其清除!
马丁

33

当我在不同的地方阅读休眠而不喜欢您分配给集合的内容时,我认为最安全的做法显然是将其设置为最终状态,如下所示:

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,至少这似乎可行。


2
为什么要调用keepAll()?为什么不先执行clear()然后再执行addAll()?
edbras 2014年

1
“为什么要调用retainAll()?为什么不先清除()再加上addAll()?” 很好的问题,我想我是在这样的假设下进行的:如果您在重新添加项目之前没有删除这些项目,休眠将不太可能将其视为数据库更新。但是我怀疑它无论如何都不行。
xpusostomos,2015年

2
您应该在clear()之上使用keepAll(),否则,如果您恰巧传入相同的set对象,则可能会抹去角色。例如:user.setRoles(user.getRoles())== user.roles.clear()
马丁

2
当您设置orphanRemoval = true并创建一个该集合为null的记录时,也会出现此错误。因此:a具有orphanremoval = true的oneToMany b。当您创建A且B = null时,将触发此问题。您初始化并使其最终化的解决方案似乎是最好的。
劳伦斯

谢谢!我正在将List初始化为List<String> list = new ArrayList<>();。更改它以List<String> list = null;解决问题:)
激进的

19

实际上,我的问题是关于实体的等于和哈希码。遗留代码会带来很多问题,永远不要忘记对其进行检查。我所做的只是保持删除-孤立策略,并纠正equals和hashcode。


9
请提供一个制作精良的equals和hashCode方法的示例?因为我有很多问题:或我无法更新我的设备集,或者出现StackOverflow错误。我在这里提出了一个问题:stackoverflow.com/questions/24737145/…。谢谢
SaganTheBest 2014年

11

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


发送新的ArrayList而不是null对我来说很有效。谢谢
rpajaziti

4

有相关类型:


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

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

使用关系类型:


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

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

4

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

3

尝试使用时遇到了这个问题TreeSet。我确实初始化oneToManyTreeSet哪个作品

@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可以在这里


也许,最好让用户Collections.emptySet()使用单例空列表
ryzhman

3

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

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}

3

当使用JSON发布请求更新实体时,我遇到了这个问题。当我更新实体时没有有关子项的数据时发生错误,即使没有子项也是如此。新增中

"children": [],

向请求主体解决了问题。


1

另一个原因可能是使用了龙目岛。

@Builder- Collections.emptyList()即使您说保存的原因.myCollection(new ArrayList());

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

我的2美分,只用了2个小时就一样了:)



1

我正在使用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;
  }

看来,即使我不覆盖收集自己,反序列化确实它的引擎盖下,引发这个问题都是一样的。解决方案是更改与反序列化器关联的设置器,以便清除列表并添加所有内容,而不是覆盖它:

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

1

我有同样的问题,但那是当集合为null时。仅在“列表”中的“集合”工作中找到。您可以尝试在JPA注释fetch = FetchType.EAGER中插入休眠注释@LazyCollection(LazyCollectionOption.FALSE)。

我的解决方案:这是我的配置,并且工作正常

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;

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将子项作为主结构的一部分而不是in _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

小心

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

这种方法也打破了休眠状态。



0

我的与Spring Boot完全不同!对我而言,这不是由于设置了收藏属性。

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

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


0

这与以前的答案相反,我遇到的错误完全相同:当我的setter函数如下所示时,“不再引用带有层叠=” all-delete-orphan”的集合...。”

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

然后当我将其更改为简单版本时,它消失了:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(休眠版本-尝试了5.4.10和4.3.11。花了几天时间尝试各种解决方案,然后再返回到setter中的简单分配。现在就为什么会这样感到困惑。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.