所以我有一个实体书
public class Book {
private String id;
private String name;
private String description;
private Image coverImage;
private Set<Chapter> chapters;
//Sets & Gets
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return Objects.equals(name, book.name) &&
Objects.equals(description, book.description) &&
Objects.equals(image, book.image) &&
Objects.equals(chapters, book.chapters);
}
@Override
public int hashCode() {
return Objects.hash(name, description, image, chapters);
}
}
实体章
public class Chapter {
private String id;
private String title;
private String number;
private LocalDate releaseDate;
private Set<Distributor> distributors;
//Sets & Gets
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Chapter)) return false;
Chapter chapter = (Chapter) o;
return Objects.equals(title, chapter.title) &&
Objects.equals(number, chapter.number) &&
Objects.equals(releaseDate, chapter.releaseDate) &&
Objects.equals(distributors, chapter.distributors);
}
@Override
public int hashCode() {
return Objects.hash(title, number, releaseDate, distributors);
}
}
和分销商实体
public class Distributor {
private String id;
private String name;
private Image logoImage;
//Sets & Gets
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Distributor)) return false;
Distributor that = (Distributor) o;
return Objects.equals(name, that.name) &&
Objects.equals(logoImage, that.logoImage);
}
@Override
public int hashCode() {
return Objects.hash(name, logoImage);
}
}
我有List<Chapter>
个新旧章节,我只需要在书中添加新的章节。
Hibernate使用其自定义实现Set<Chapter>
来获取和填充数据库中所有章节的PersistentSet
。
我试图解决的问题是只添加List中那些PersistentSet中不存在的章节。尽管如此,由于Chapter不使用id字段来计算hashCode / equals,我可以将List中的所有章节添加到PersistentSet中,结果应该是一个Set,它从已经存在和包含的列表中排除那些那些不在集合中的人。嗯......这没有发生。
Hibernate的PersistentSet
没有使用我为Distributor
实体定义的hashCode / equals函数,而是使用它的一些内部实现,导致List和Set的章节具有不同的hashCodes并没有平等。让我们从List lChapter
中调用一个章节,从PersistentSet psChapter
调用一章,并假设它们是Id,除了Id。
如果我做
lChapter.equals(psChapter); //True
但如果我这样做
psChapter.equals(lChapter); //False
如果我这样做
book.getChapters().addAll(chapters);
成为book
附加实体,有20章,chapters
列表有21章,结果是一套41章。
我在这里做错了什么?我发现这是一个非常微不足道的问题,但我还没有找到任何解决方案,并不会让我通过列表并在添加之前检查它是否包含在内。这是我无法承受的不必要的额外步骤。
编辑1:图像是一个自定义实现,并且确实实现了hashCode / equals,并且已经证明它不是问题所在。即使我从实体中删除了上述实验结果也没有改变。
编辑2:我调试了代码,如果你进入章节的等号函数的lChapter.equals(psChapter);
,我会在Objects.equals(distributors, chapter.distributors)
进行调试,它会转到分配器等于函数而{{1}它进入PersistentSet。
答案 0 :(得分:0)
<强>更新强>
在浏览了休眠JIRA PersistentSet does not honor hashcode/equals contract when loaded eagerly 后,这是一个已知问题。
在某些情况下急切加载的集合会在填充字段值之前调用其项目上的hashcode,因此其他方法(如contains(),remove()等会受到影响。
修复计划用于6.0.0 Alpha。
根据JIRA中的一项建议作为解决方法,坚持使用LAZY系列要好得多。 EAGER提取对性能有害,可能导致笛卡尔产品,并且您无法对该集合进行分页。
这应该解释为什么
| lChapter.equals(psChapter); `//是的 因为它使用普通的Set.equals
psChapter.equals(lChapter);
//错误
这通过PersistentSet进行,同时违反了hashcode / equals合约,因此即使元素存在于Set中,也不能保证返回true。而且它还允许允许向Set添加重复元素。
答案 1 :(得分:0)
我尝试使用main方法编写代码,它按预期工作,如下所示
你可以试试你的代码吗?
public static void main(String[] args) {
LocalDate now = LocalDate.now();
Distributor db = new Distributor();
db.setId("1");
db.setLogoImage(null);
db.setName("Name");
Set<Distributor> dbs = new HashSet<>();
dbs.add(db);
Chapter c= new Chapter();
c.setDistributors(dbs);
c.setId("1");
c.setNumber("123");
c.setReleaseDate(now);
c.setTitle("10:30");
Set<Chapter> chapters = new HashSet<>();
chapters.add(c);
Book b= new Book();
b.setChapters(chapters);
b.setCoverImage(null);
b.setDescription("Description");
b.setId("1");
b.setName("Name");
Set<Distributor> dbs1 = new HashSet<>();
Distributor db1 = new Distributor();
db1.setId("1");
db1.setLogoImage(null);
db1.setName("Name");
dbs1.add(db1);
Chapter c1 = new Chapter();
c1.setDistributors(dbs1);
c1.setId("1");
c1.setNumber("123");
c1.setReleaseDate(now);
c1.setTitle("10:30");
System.out.println(chapters.add(c1));
System.out.println(chapters.size());
}
答案 2 :(得分:0)
此问题的一种解决方法是将@OrderColumn添加到Book与Chapter之间的关系中,如下所示:
public class Book {
private String id;
private String name;
private String description;
private Image coverImage;
@OrderColumn
private Set<Chapter> chapters;
...
}
这将摆脱PersistentSet,而是使用不违反equals / hashCode契约的Hibernate的PersistentSortedSet。
顺便说一句,我想知道为什么您没有@OneToMany / @ManyToMany批注?似乎Hibernate会自动执行(如果发现令人毛骨悚然)。 Hibernate如何确定这是一对多关系还是多对多关系?