Hibernate的PersistentSet不使用hashCode / equals

时间:2018-03-19 04:08:51

标签: 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 (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。

3 个答案:

答案 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如何确定这是一对多关系还是多对多关系?