Java 冬眠';s PersistentSet未使用hashCode/equals的自定义实现
所以我有一本实体书Java 冬眠';s PersistentSet未使用hashCode/equals的自定义实现,java,spring,hibernate,Java,Spring,Hibernate,所以我有一本实体书 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 (t
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);
}
}
我有一个新旧章节的列表
,我只需要在书中添加新章节。
Hibernate使用其自定义实现PersistentSet
获取并填充数据库中的所有章节
我试图解决的问题是只从列表中添加PersistentSet中不存在的章节。但是,对于这一点,由于Chapter不使用id字段来计算hashCode/equals,我可以将列表中的所有章节添加到PersistentSet,结果应该是一个集合,该集合将已经存在的章节从列表中排除,并包含不在集合中的章节。好。。。这并没有发生
Hibernate的PersistentSet
没有使用我为Distributor
实体定义的hashCode/equals函数,而是使用它的一些内部实现,导致列表和集合中的章节具有不同的hashCode,并且不相等。让我们调用列表lChapter
中的一个章节和PersistentSetpsChapter
中的一个章节,并假设它们除了Id之外是相等的。
如果我这样做
但如果我这样做了
psChapter.equals(lChapter); //False
如果我这样做了
book.getChapters().addAll(chapters);
由于书籍
是一个包含20章的附加实体,而章节
列表包含21章,因此结果是一个包含41章的集合
我做错什么了?我发现这是一个非常小的问题,但我还没有找到任何解决方案,不涉及我检查列表,并检查它是否包含在添加之前。这是不必要的额外步骤,我负担不起
编辑1:Image是一个自定义实现,它实现了hashCode/equals,并且已经证明它不是问题所在。即使我从实体中移除,上述实验结果也不会改变
编辑2:我调试了代码,在执行
lChapter.equals(psChapter)时
如果进入章节的equals函数的Objects.equals(distributors,chapter.distributors)
,它将进入Distributor equals函数,而在psChapter.equals(lChapter)上代码>它进入到PersistentSet中。更新
在经历了hibernate JIRA之后,这是一个已知的问题
在某些情况下,急切加载的集合在填充其字段值之前对其项调用hashcode,从而影响其他方法,如contain()、remove()等
计划对6.0.0 Alpha进行修复
根据JIRA中的一条建议,作为一种解决方法,坚持使用惰性集合要好得多。即时抓取不利于性能,可能导致笛卡尔积,并且无法对集合进行分页
这应该可以解释为什么
Chapter.equals(psChapter)
返回true
,因为它使用normal Set.equals
psChapter.equals(lChapter)代码>返回false
这通过PersistentSet进行,同时违反hashcode/equals约定,因此即使元素存在于集合中,也不能保证返回true。此外,它还允许向集合中添加重复的元素。我用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());
}
publicstaticvoidmain(字符串[]args){
LocalDate now=LocalDate.now();
分发服务器db=新分发服务器();
db.setId(“1”);
db.setLogoImage(空);
db.setName(“名称”);
Set dbs=new HashSet();
添加(db);
第c章=新的一章();
c、 SET分销商(dbs);
c、 setId(“1”);
c、 设定编号(“123”);
c、 设置发布日期(现在);
c、 片名(“10:30”);
Set chapters=newhashset();
增加(c)章;
书b=新书();
b、 第三章(章);
b、 setCoverImage(空);
b、 设定说明(“说明”);
b、 setId(“1”);
b、 设置名称(“名称”);
Set dbs1=新的HashSet();
分发服务器db1=新分发服务器();
db1.setId(“1”);
db1.setLogoImage(空);
db1.集合名(“名称”);
dbs1.add(db1);
第c1章=新的一章();
c1.SET分销商(dbs1);
c1.setId(“1”);
c1.设定编号(“123”);
c1.设置发布日期(现在);
c1.片名(“10:30”);
系统输出打印LN(章节添加(c1));
System.out.println(chapters.size());
}
此问题的解决方法是在书籍和章节之间的关系中添加一个@OrderColumn
,如下所示:
public class Book {
private String id;
private String name;
private String description;
private Image coverImage;
@OrderColumn
private Set<Chapter> chapters;
...
}
公共课堂教材{
私有字符串id;
私有字符串名称;
私有字符串描述;
私人影像;
@订单列
专用章节;
...
}
这将去掉PersistentSet
,而是使用Hibernate的PersistentSortedSet
,它不会违反equals/hashCode契约
顺便问一下,我想知道为什么您没有任何@OneToMany
/@manytomy
注释?似乎Hibernate会自动完成(我觉得很恐怖)。Hibernate是如何决定这是一对多还是多对多关系的?这些新章节有相同的分发对象吗?是的,它们完全相同。正如我所说,唯一的区别是Id字段。编辑帖子以包含相关信息。lChapter
和psChapter
分别是章节
或列表
和集合
的实例吗?都是章节,都是相等的。psChapter从hibernate填充,lChapter从列表中获取。列表和集合包含的章节基本相同。就像我上面的例子。如果说列表有21章,21章中的20章已经在数据库中,但1章在列表中,两者都不是附加实体。另一方面,该集合有20个章节(与列表中的20个章节相同,但这是PersistentSet中的附加实体)。如果
book.getChapters().addAll(chapters);
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());
}
public class Book {
private String id;
private String name;
private String description;
private Image coverImage;
@OrderColumn
private Set<Chapter> chapters;
...
}