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

axc*_*dnt 194 java hibernate hibernate-mapping

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

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".
Run Code Online (Sandbox Code Playgroud)

我有一个父实体,它有Set<...>一些子实体.当我尝试更新它时,我将所有引用设置为此集合并进行设置.

以下代码表示我的映射:

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

我试图只清理Set <..>,根据这个:如何"可能"解决问题,但它不起作用.

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

谢谢!

bra*_*mus 194

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

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

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

要添加孩子:

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}
Run Code Online (Sandbox Code Playgroud)

要删除孩子:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,我的问题是关于我的实体的equals和hashcode.遗留代码可能带来很多问题,永远不要忘记检查它.我所做的只是保持delete-orphan策略并纠正equals和hashcode. (7认同)
  • 我很高兴你解决了你的问题.equals和hashcode用Hibernate咬了我几次.不要使用"[已解决]"更新问题的标题,而应该继续发布您的答案,然后将其标记为已接受的答案. (3认同)
  • **通常,您只想在构造函数中一次“新”集合。每当您想在列表中添加或删除某些内容时,都必须修改列表的内容,而不是分配新的列表。** (2认同)

Man*_*anu 96

方法:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}
Run Code Online (Sandbox Code Playgroud)

如果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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在sonEntities.clear()上使用this.sonEntities.retainAll(aSet),因为如果aSet == this.sonEntities(即同一个对象),你将在添加之前清除该集合! (6认同)
  • @Skuld 我有一个类似的问题,我应用了你的解决方案(在 setter 方法中,我清除了孩子集合 - this.children.clear() - 我添加了新孩子 - this.children.addAll(children))。此更改并没有解决我的问题。我仍然得到“拥有级联的集合=“all-delete-orphan”不再被拥有实体实例引用”异常。你知道为什么吗?非常感谢! (2认同)

xpu*_*mos 26

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

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

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}
Run Code Online (Sandbox Code Playgroud)

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

事实证明,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);
  }
}
}
Run Code Online (Sandbox Code Playgroud)

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

  • 你为什么要调用retainAll()?为什么不 clear() 后跟 addAll()? (2认同)
  • 您应该使用 retainAll() 而不是 clear() 否则,如果您碰巧传入相同的 set 对象,则可能会消除角色。例如: user.setRoles(user.getRoles()) == user.roles.clear() (2认同)
  • 当您设置orphanRemoval = true并且您创建了此集合为空的记录时,您也会收到此错误.所以:a具有oneToMany b和orphanremoval = true.当您创建A,其中B = null时,将触发此问题.您初始化并使其成为最终解决方案似乎是最好的. (2认同)

axc*_*dnt 16

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

  • 请问,一个做得好的equals和hashCode方法的例子?因为我有很多问题:或者我无法更新我的设置或我得到StackOverflow错误.我在这里打开了一个问题:http://stackoverflow.com/questions/24737145/equals-and-hashcode-of-these-entities-spring-mvc-hibernate.谢谢 (9认同)

Kon*_*rer 9

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

  • 发送新的 ArrayList 而不是 null 对我有用。谢谢 (2认同)

Ous*_*ama 7

我通过这样做修复了:

1. clear existing children list so that they are removed from database

parent.getChildren().clear();
Run Code Online (Sandbox Code Playgroud)

2. add the new children list created above to the existing list

parent.getChildren().addAll(children);
Run Code Online (Sandbox Code Playgroud)

Hope this post will help you to resolve the error


Mic*_*mal 6

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

请注意这两个休眠问题:


小智 6

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

"children": [],
Run Code Online (Sandbox Code Playgroud)

到请求正文解决了问题。


Ion*_*Man 6

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

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

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

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

我只是删除了orphanRemoval,更新就成功了。


luw*_*zek 5

我使用@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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我有同样的问题,但那是当集合为空时。仅在 Set 集合中,在 List 中工作正常。您可以尝试使用hibernate注释@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;
Run Code Online (Sandbox Code Playgroud)