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

"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<..只有>,根据这个:如何“可能”解决问题,但它没有工作。

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

谢谢!


当前回答

@OneToMany(mappedBy = "entity_b", cascade = [CascadeType.]所有],orphanRemoval = true)

我也遇到了同样的错误。当我删除上面结构中的;"orphanRemoval = true"部分时,它工作了

其他回答

下面的解决方案对我很有效

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

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

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

也许是Intellij缓存问题?

跟进:

The problem appears when running tests using the maven-surefire-plugin reuseForks true. Using reuseForks false would provide a quick fix, but the tests running time will increase dramatically. Because we are reusing forks, the database context might become dirty due to other tests that are run - without cleaning the database context afterwards. The obvious solution would be to clean the database context before running a test, but the best one should be to clean up the database context after each test (solving the root cause of the original problem). Using the @Transactional annotation on your test methods will guarantee that your database changes are rolled back at the end of the test methods. See the Spring documentation on transactions: https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-tx.

当我在很多地方读到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,这至少看起来是可行的。

有一个类似的bug: https://hibernate.atlassian.net/browse/HHH-9940。

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

有2个可能的解决方案:

集合初始化为空集合(而不是null) orphanRemoval设置为false

例如- was:

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

变成:

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