Java JPA@OneToMany-集合与列表-使用集合时无法从双向关联中删除一个子实体

Java JPA@OneToMany-集合与列表-使用集合时无法从双向关联中删除一个子实体,java,hibernate,jpa,spring-data-jpa,one-to-many,Java,Hibernate,Jpa,Spring Data Jpa,One To Many,如果使用了Set,则无法从OneToMany关联中删除子实体。如果我使用List而不是Set Post.java @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) private List<Comment> comments; public void addComment(Comment comment) { if (comments == null) {

如果使用了
Set
,则无法从OneToMany关联中删除子实体。如果我使用
List
而不是
Set

Post.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);
}
示例应用程序的代码可在

  • master
    分支正在使用
    Set
    ,测试用例失败
  • list
    分支正在使用“list”和测试用例通过

我不确定我是否犯了什么错误。请建议。

您的测试根据
帖子评论的大小进行断言。它也是
@Transactional
,意思是
postRepository.getOne()
返回与持久性上下文中已有的
Post
完全相同的实例。这意味着
dbPost==post
,因此您的问题与JPA无关,它纯粹是一个Java问题

尝试在
注释处放置断点。删除(注释)
行。您可能会看到,对于
Set
版本,它返回
false
。这是因为没有正确实现
hashCode/equals
。尝试使用
comments.removeIf(element->element.getId().equals(comment.getId())
来查看问题是否消失

(作为旁注:JPA实体可以通过多种方式实现
equals/hashCode
,但根据经验,将其与被引用实体进行比较不是一个好主意,尤其是当被引用实体应该被延迟获取时)

编辑:

代码对
列表
有效,而对
无效的原因是:

  • 列表
    根本不使用
    hashCode
  • 查找元素时,
    HashSet
    使用
    hashCode
    前面的
    equals
    值来确定潜在的相等性
  • 更新元素时,已在
    HashSet
    中的元素的
    hashCode
    不会自动重新计算

在测试中,当您向
Post
添加
Comment
时,每个注释的
hashCode
都是基于
Post.id
null
Post.id
变为非空,并且您试图删除的
注释的
hashCode
。因此,
remove
无法在
集中找到与(新计算的)相同(原始)
hashCode
的元素
hashCode
用于作为参数传递的
注释
这是设计的事实,
equals
将为其中一个元素返回
true
列表
没有这个问题,因为它用于元素比较的所有内容都是
equals
谢谢您的帮助响应。当我调试该问题时,我确实观察到该问题是由于对象相等性为false造成的。但我不确定当我将
Set
替换为
List
时,为什么相同的代码会起作用。谈到
equals/hashcode
实现,我更愿意只使用
@Id
字段。但由于我使用的是UUID标识符,我不确定无法保存包含多条
注释的
帖子
(在对象被持久化之前,ID保持为
null
,因此只保存一条注释)回答得好,我也认为是一样的
@ManyToOne(fetch = FetchType.LAZY)
@EqualsAndHashCode.Include
private Post post;