ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

我已经尝试清洁套装<..>只是,根据这个: How to "possible" solve the problem 但它没有用。

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

谢谢!

@mel3kings,您提供的链接不再有效。
删除元素时尝试使用可变集合。例如,如果 manyotherList<T>,则不要使用 something.manyother.remove(other)。使许多其他可变的,例如 ArrayList<T> 并使用 orphanDelete = true
有一个看起来非常相似的错误:hibernate.atlassian.net/browse/HHH-9940 以及重现它的代码:github.com/abenneke/sandbox/tree/master/…
链接“如何“可能”解决问题”不再可用

b
brainimus

检查您为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);
}

实际上,我的问题是关于我的实体的 equals 和 hashcode。遗留代码会带来很多问题,永远不要忘记检查它。我所做的只是保持删除孤儿策略和正确的等号和哈希码。
我很高兴你解决了你的问题。 equals 和 hashcode 用 Hibernate 咬了我几次。而不是用“[已解决]”更新问题的标题,您应该继续发布您的答案,然后将其标记为已接受的答案。
谢谢,我遇到了类似的事情,结果我的 setter 是空的..
是的,即使在空列表上也会出现此错误。我没想到会这样,因为没有孤儿(列表为空)。
通常你只想在构造函数中“新建”一次集合。任何时候您想在列表中添加或删除某些内容时,您都必须修改列表的内容,而不是分配新列表。大多数小鬼
C
Community

方法:

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

@Skuld我有一个类似的问题,我应用了你的解决方案(在setter方法中我清除了孩子的集合 - this.children.clear() - 我添加了新的孩子 - this.children.addAll(children))。此更改没有解决我的问题。我仍然得到“拥有实体实例不再引用具有 cascade="all-delete-orphan" 的集合”异常。你知道为什么吗?非常感谢!
@ovdsrn对不起,这不是我的答案,我只是整理了答案的格式,原作者(kmmanu)可能会帮助你(或者如果你的情况不同,你可能想开始一个新问题这里提出的原始问题)祝你好运
这很好用,但当子级包含在嵌套的休眠组件中并且该组件在其实体中为空时,效果就不太好了。然后你会得到同样的错误。这是因为子实体的所有者是根实体,而不是被设为 null 的组件......这样,组件永远不允许变为 null,而是应该导致转发到孩子...至少,我不知道更好的解决方案...这是一种集合 clear() 构造,然后通过 destroy() 在组件上...
有关上述更多详细信息,另请参阅此帖子:stackoverflow.com/questions/4770262/…
使用 this.sonEntities.retainAll(aSet) 而不是 sonEntities.clear() 因为如果 aSet == this.sonEntities (即同一个对象),您将在添加之前清除集合!
x
xpusostomos

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

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,这至少似乎有效。


为什么要调用retainAll()?为什么不 clear() 后跟 addAll()?
“你为什么要调用retainAll()?为什么不先clear()再调用addAll()?”好问题,我认为我的工作假设是如果您在再次添加项目之前没有删除这些项目,那么休眠将不太可能将其视为数据库更新。但我怀疑它无论如何都不会那样工作。
您应该使用 retainAll() 而不是 clear() 否则如果您碰巧传入相同的 set 对象,您可能会清除角色。例如:user.setRoles(user.getRoles()) == user.roles.clear()
当您设置 orphanRemoval=true 并创建此集合为空的记录时,您也会收到此错误。所以:a 有 oneToMany b 与 orphanremoval = true。当您创建 A where B = null 时,会触发此问题。您初始化并使其成为最终的解决方案似乎是最好的。
谢谢!我将我的列表初始化为 List<String> list = new ArrayList<>();。将其更改为 List<String> list = null; 解决了问题:)
a
axcdnt

实际上,我的问题是关于我的实体的 equals 和 hashcode。遗留代码会带来很多问题,永远不要忘记检查它。我所做的只是保持删除孤儿策略和正确的等号和哈希码。


请问,一个制作精良的equals和hashCode方法的例子?因为我有很多问题:或者我无法更新我的设置,或者我收到 StackOverflow 错误。我在这里提出了一个问题:stackoverflow.com/questions/24737145/…。谢谢
O
Ousama

我通过这样做来修复:

1.清除现有的子列表,以便将它们从数据库中删除

parent.getChildren().clear();

2. 将上面创建的新子列表添加到现有列表中

parent.getChildren().addAll(children);

希望这篇文章能帮助您解决错误


为我工作....谢谢。
@KPK 不客气
K
Konsumierer

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


发送新的 ArrayList 而不是 null,对我有用。谢谢
c
co ting

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

"children": [],

到请求正文解决了这个问题。


l
luwojtaszek

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

M
Michał Stochmal

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

注意这两个休眠问题:

https://hibernate.atlassian.net/browse/HHH-10708

https://hibernate.atlassian.net/browse/HHH-11459


使用 5.5.7 仍然会发生。当要合并的实体从 Web 会话反序列化时会出现最终问题,例如,当将更改从 jsf 前端合并到应该进行合并的后端服务时,将 enableLazyInitialization 设置为 false 可以解决此错误,但使用缺点是不能使用 LazyInitialization 功能,这有点烦人
I
IgniteCoders

有关系类型:

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

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

使用关系类型:

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

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

A
Arlo Guthrie

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

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

I
IonicMan

所有这些答案都没有帮助我,但我找到了另一个解决方案。

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

我试图更新实体 A 和 B。它有效。但是在更新实体 C 时,我收到了上述错误。在实体 BI 中有这样的注释:

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

我只是删除了 orphanRemoval 并且更新有效。


嗨@IonicMan 现在我在删除 orphanRemoaval 后没有遇到错误,但我收到了下面提到的新错误。对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例
@Tejal 太棒了,很高兴我能帮上忙
C
Community

我在尝试使用 TreeSet 时遇到了这个问题。我确实使用 TreeSet 初始化了 oneToMany,它有效

@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


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

有一个看起来非常相似的错误:https://hibernate.atlassian.net/browse/HHH-9940

以及重现它的代码:https://github.com/abenneke/sandbox/tree/master/hibernate-null-collection/src/test

对此有 2 个可能的解决方法:

集合初始化为空集合(而不是 null)

orphanRemoval 设置为 false

示例 - 是:

@OneToMany(cascade = CascadeType.REMOVE,
        mappedBy = "jobEntity", orphanRemoval = true)
private List<JobExecutionEntity> jobExecutionEntities;

变成:

@OneToMany(cascade = CascadeType.REMOVE,
        mappedBy = "jobEntity")
private List<JobExecutionEntity> jobExecutionEntities;

集合初始化为空集合对我有用
A
Apostolos

我有同样的问题,但它是当集合为空时。只有在 Set 集合中,在 List 中才能正常工作。您可以尝试使用休眠注释 @LazyCollection(LazyCollectionOption.FALSE) 而不是 JPA 注释 fetch = FetchType.EAGER.

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

@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;

J
Jan Zyka

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

@Builder - 即使您说 .myCollection(new ArrayList()); 也会导致保存 Collections.emptyList()

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

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


J
Justinas Jakavonis

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

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


S
Sofia Paixão

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

似乎即使我自己没有覆盖集合,反序列化也在幕后完成,同样触发了这个问题。解决方案是更改与反序列化器关联的设置器,以便它清除列表并添加所有内容,而不是覆盖它:

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

m
madz

我的与 Spring Boot 完全不同!对我来说,这不是由于设置集合属性。

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

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


测试方法上的 @Transactional 注释意味着数据库上下文将在测试方法结束时回滚,无论其结果如何。
O
Oleg Ushakov
@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);

现在这个孩子也恢复了其他细节,而且效果很好。


r
razvanone

从 [Intellij Idea] 版本 2020.3 批量运行测试时,spring-boot 2.4.1 出现此问题。从 IntelliJ 一次仅运行一个测试或从命令行运行测试时,不会出现此问题。

也许是 Intellij 缓存问题?

跟进:

使用 maven-surefire-plugin reuseForks true 运行测试时会出现问题。使用reuseForks false 将提供快速修复,但测试运行时间将显着增加。因为我们正在重用 fork,所以数据库上下文可能会由于运行其他测试而变脏 - 之后没有清理数据库上下文。显而易见的解决方案是在运行测试之前清理数据库上下文,但最好的解决方案应该是在每次测试之后清理数据库上下文(解决原始问题的根本原因)。在您的测试方法上使用 @Transactional 注释将保证您的数据库更改在测试方法结束时回滚。请参阅有关事务的 Spring 文档:https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-tx


H
Himanshu Suthar

我在父实体中使用这些注释时遇到了类似的问题:

@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })

错误地,我试图在数据库中保存一个空父对象并为我的实体对象正确设置值解决了我的错误。因此,请检查您是否愚蠢地设置了错误的值或尝试在数据库中保存空对象。


S
Snekse

添加我愚蠢的答案。我们正在使用 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

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


p
priyanka_rao

以下解决方案对我有用

//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;

M
Marcin Szymczak

而不是分配新的集合

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

将所有元素替换为

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

D
Diógenes Ricardo

小心

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

这种方法也打破了休眠。


l
ligett

这与之前的答案相反,我遇到了完全相同的错误:“不再引用具有 cascade=”all-delete-orphan” 的集合......”当我的 setter 函数看起来像这样时:

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 中的简单分配。现在很困惑为什么会这样。)


A
Andrii Bobrov

在我的例子中,它是从多个线程并发访问一个 Hibernate Session。我有 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; 
}

批处理:reader -> processor -> writer 在一个事务中。

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

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

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


a
adlerer

截至 2021 年和 Spring Boot 2.5,它帮助我在声明字段时立即对其进行初始化:

@OneToMany(mappedBy="target",fetch= FetchType.EAGER,cascade = CascadeType.ALL, orphanRemoval = true)
private List<TargetEntity> targets = new ArrayList<>();

A
Amalan T

当我们将孩子设置为最终时,问题就解决了。

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


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅