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

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

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

谢谢!


当前回答

从[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.

其他回答

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

检查所有你给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);
}

有一个类似的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;
@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);

现在孩子复活了其他细节以及它工作得很好。

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

"children": [],

向请求体解决了问题。