[英]JPA @OneToMany - Set vs List - Not able to delete one child entity from bidirectional association when using Set
Not able to delete child entity from OneToMany association if Set
is used.如果使用Set
,则无法从 OneToMany 关联中删除子实体。 Everything works fine if I use List
instead of Set
如果我使用List
而不是Set
一切正常
Post.java帖子.java
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments;
public void addComment(Comment comment) {
if (comments == null) {
comments = new ArrayList<>();
}
comment.setPost(this);
comments.add(comment);
}
public void removeComment(Comment comment) {
if (comments == null || comments.isEmpty()) {
return;
}
comments.remove(comment);
comment.setPost(null);
}
Comment.java评论.java
@ManyToOne(fetch = FetchType.LAZY)
@EqualsAndHashCode.Include
private Post post;
Code for sample application is available on Github Github上提供了示例应用程序的代码
master
branch is using Set
and test case fails master
分支正在使用Set
并且测试用例失败list
branch is using 'List` and test case passes list
分支正在使用“列表”并且测试用例通过Not sure whether I am making any mistake.不知道我是否犯了任何错误。 Please suggest.请建议。
Your test asserts on the size of Post.comments
.您的测试断言Post.comments
的大小。 It is also @Transactional
, meaning postRepository.getOne()
returns the exact same instance of Post
that you already have in the persistence context.它也是@Transactional
,这意味着postRepository.getOne()
返回您在持久性上下文中已经拥有的完全相同的Post
实例。 This means dbPost == post
, and so your problem has nothing to do with JPA, it is purely a Java problem.这意味着dbPost == post
,因此您的问题与 JPA 无关,纯粹是 Java 问题。
Try putting a breakpoint at the comments.remove(comment)
line.尝试在comments.remove(comment)
行设置断点。 What you'll likely see is that it returns false
for the Set
version.您可能会看到它为Set
版本返回false
。 That's because hashCode/equals
is not implemented correctly.那是因为hashCode/equals
没有正确实现。 Try using comments.removeIf(element -> element.getId().equals(comment.getId())
instead to see if the problem goes away.尝试使用comments.removeIf(element -> element.getId().equals(comment.getId())
来查看问题是否消失。
(As a side note: there are multiple ways equals/hashCode
can be implemented for JPA entities, but as a rule of thumb, making it compare a referenced entity is not a good idea, especially when that referenced entity is supposed to be lazily fetched). (附带说明:对于 JPA 实体,可以通过多种方式实现equals/hashCode
,但根据经验,比较引用的实体并不是一个好主意,尤其是当引用的实体应该被延迟获取时)。
EDIT:编辑:
The reason why the code works for List
, and fails for Set
is that:代码对List
有效而对Set
无效的原因是:
List
doesn't use hashCode
at all,一个List
根本不使用hashCode
,HashSet
uses the hashCode
value before equals
to determine potential equality在查找元素时, HashSet
使用equals
之前的hashCode
值来确定潜在的相等性hashCode
of an element already in the HashSet
is not recalculated automatically when that element is updated.更新该元素时,不会自动重新计算HashSet
中已有元素的hashCode
。 In the test, when you add Comment
s to the Post
, the hashCode
of each comment is calculated based on the Post.id
being null
.在测试中,当你在Post
中添加Comment
时,每条评论的hashCode
是根据Post.id
为null
计算的。 However, as soon as you call entityManager.flush()
, Post.id
becomes non-null, and the hashCode
of the Comment
you're trying to remove changes.但是,只要您调用entityManager.flush()
, Post.id
就会变为非空,并且您尝试删除更改的Comment
的hashCode
。 Hence, remove
cannot find an element in the Set
with the same (original) hashCode
as the (newly calculated) hashCode
for the comment
passed as the parameter.因此,对于作为参数传递的comment
, remove
无法在Set
中找到具有与(新计算的) hashCode
相同(原始) hashCode
的元素。 This is desipte the fact that equals
would have returned true
for one of the elements .这是因为对于其中一个元素, equals
会返回true
的事实。 A List
does not have this problem, because all it ever uses for element comparison is equals
. List
没有这个问题,因为它用于元素比较的只是equals
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.