尝试更新我的实体时遇到以下问题:
"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 但它没有用。
如果您有任何想法,请告诉我。
谢谢!
manyother
是 List<T>
,则不要使用 something.manyother.remove(other)
。使许多其他可变的,例如 ArrayList<T>
并使用 orphanDelete = true
检查您为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);
}
方法:
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);
}
}
当我在不同的地方读到 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,这至少似乎有效。
List<String> list = new ArrayList<>();
。将其更改为 List<String> list = null;
解决了问题:)
实际上,我的问题是关于我的实体的 equals 和 hashcode。遗留代码会带来很多问题,永远不要忘记检查它。我所做的只是保持删除孤儿策略和正确的等号和哈希码。
我通过这样做来修复:
1.清除现有的子列表,以便将它们从数据库中删除
parent.getChildren().clear();
2. 将上面创建的新子列表添加到现有列表中
parent.getChildren().addAll(children);
希望这篇文章能帮助您解决错误
我有同样的错误。我的问题是,在保存实体后,映射的集合仍然为空,并且在尝试更新实体时抛出了异常。对我有什么帮助:保存实体,然后进行刷新(集合不再为空),然后执行更新。也许用 new ArrayList() 或其他东西初始化集合也可能有帮助。
我在使用 JSON 发布请求更新实体时遇到了这个问题。当我更新没有关于孩子的数据的实体时发生错误,即使没有孩子。添加
"children": [],
到请求正文解决了这个问题。
我使用@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);
}
}
}
}
它可能是由 hibernate-enhance-maven-plugin
引起的。当我启用 enableLazyInitialization
属性时,这个异常开始发生在我的惰性集合上。我正在使用休眠 5.2.17.Final。
注意这两个休眠问题:
https://hibernate.atlassian.net/browse/HHH-10708
https://hibernate.atlassian.net/browse/HHH-11459
有关系类型:
当集合在 hasMany
中声明时,不要尝试实例化集合,只需添加和删除对象。
class Parent {
static hasMany = [childs:Child]
}
使用关系类型:
但是只有在声明为属性(使用关系)并且未在声明中初始化时,集合才能为空。
class Parent {
List<Child> childs = []
}
我唯一一次收到此错误是当我尝试将 NULL 传递给集合的设置器时。为了防止这种情况,我的二传手看起来像这样:
public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
if(submittedForms == null) {
this.submittedForms.clear();
}
else {
this.submittedForms = submittedForms;
}
}
所有这些答案都没有帮助我,但我找到了另一个解决方案。
我有一个实体 A 包含实体 B 的列表。实体 B 包含实体 C 的列表。
我试图更新实体 A 和 B。它有效。但是在更新实体 C 时,我收到了上述错误。在实体 BI 中有这样的注释:
@OneToMany(mappedBy = "entity_b", cascade = [CascadeType.ALL] , orphanRemoval = true)
var c: List<EntityC>?,
我只是删除了 orphanRemoval
并且更新有效。
我在尝试使用 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
有一个看起来非常相似的错误: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;
我有同样的问题,但它是当集合为空时。只有在 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;
另一个原因可能是使用 lombok。
@Builder
- 即使您说 .myCollection(new ArrayList());
也会导致保存 Collections.emptyList()
@Singular
- 忽略类级别默认值并保留字段 null
,即使类字段被声明为 myCollection = new ArrayList()
我的 2 美分,只花了 2 个小时,同样的 :)
当我设置 parent.setChildren(new ArrayList<>())
时,我得到了 A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance
。当我更改为 parent.getChildren().clear()
时,它解决了问题。
我正在使用 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);
}
我的与 Spring Boot 完全不同!对我来说,这不是由于设置集合属性。
在我的测试中,我试图创建一个实体,并为另一个未使用的集合收到此错误!
经过这么多尝试,我刚刚在测试方法上添加了一个 @Transactional
并解决了它。不要没有原因。
@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);
现在这个孩子也恢复了其他细节,而且效果很好。
从 [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。
我在父实体中使用这些注释时遇到了类似的问题:
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
错误地,我试图在数据库中保存一个空父对象并为我的实体对象正确设置值解决了我的错误。因此,请检查您是否愚蠢地设置了错误的值或尝试在数据库中保存空对象。
添加我愚蠢的答案。我们正在使用 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
中。对父级的更新会导致问题。
以下解决方案对我有用
//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;
而不是分配新的集合
public void setChildren(Set<ChildEntity> children) {
this.children = children;
}
将所有元素替换为
public void setChildren(Set<ChildEntity> children) {
Collections.replaceAll(this.children,children);
}
小心
BeanUtils.copyProperties(newInsum, insumOld,"code");
这种方法也打破了休眠。
这与之前的答案相反,我遇到了完全相同的错误:“不再引用具有 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 中的简单分配。现在很困惑为什么会这样。)
在我的例子中,它是从多个线程并发访问一个 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 的多线程访问是不安全的。
截至 2021 年和 Spring Boot 2.5,它帮助我在声明字段时立即对其进行初始化:
@OneToMany(mappedBy="target",fetch= FetchType.EAGER,cascade = CascadeType.ALL, orphanRemoval = true)
private List<TargetEntity> targets = new ArrayList<>();
当我们将孩子设置为最终时,问题就解决了。
我们不应该在构造函数和 setter 中更改子对象的引用。
不定期副业成功案例分享