为了账号安全,请及时绑定邮箱和手机立即绑定

如何使用@NaturalId 正确覆盖 Hibernate 实体的等于

如何使用@NaturalId 正确覆盖 Hibernate 实体的等于

慕工程0101907 2022-07-20 10:58:00
已经多次讨论过如何为实体重新定义 equals/hashCode。我的问题是关于是否需要使用 equals 中的所有字段。考虑两种情况。当我们将所有字段用于等于时:@Entitypublic class Book {    @Id    @Column    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long id;    @NaturalId    @Column(name = "isbn", nullable = false, unique = true)    private String isbn;    @Column    private String name;    private Book() {    }    public Book(String isbn) {        this.isbn = isbn;    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Book book = (Book) o;        return Objects.equals(id, book.id) &&                Objects.equals(isbn, book.isbn) &&                Objects.equals(name, book.name);    }    @Override    public int hashCode() {        return Objects.hash(isbn);    }}并测试:public class BookTest1 {    @PersistenceContext    protected EntityManager em;    @Test    public void fromTransientToManageSameEntity() {        Book book1 = new Book("4567-5445-5434-3212");        Book book2 = new Book("4567-5445-5434-3212");        em.persist(book2);        flushAndClean();        assertThat(book1, is(not((equalTo(book2))))); // not equals    }}正如我们所看到的,当将实体从瞬态转换为管理状态时 - 相同的实体将不相等。另一种情况是当我们只使用等于@NaturalId 时:@Overridepublic boolean equals(Object o) {    if (this == o) return true;    if (o == null || getClass() != o.getClass()) return false;    Book book = (Book) o;    return Objects.equals(isbn, book.isbn);}并测试:public class BookTest2 {    @PersistenceContext    protected EntityManager em;    @Test    public void fromTransientToManageSameEntity() {        Book book1 = new Book("4567-5445-5434-3212");        Book book2 = new Book("4567-5445-5434-3212");        em.persist(book2);        flushAndClean();        assertThat(book1, equalTo(book2)); // equals    }}正如我们所看到的,现在两个实体将是平等的。我的问题是同一实体在过渡到管理状态时是否应该平等。因此,在这种情况下如何正确地重新定义等号。
查看完整描述

2 回答

?
三国纷争

TA贡献1804条经验 获得超7个赞

根据这篇文章 equalshashCode应该与状态无关。如果你只覆盖了第一个,那就不好了,可能会导致奇怪的错误。他们需要有一份合同

最简单的方法是使用 lombok - 用 注释您的类,@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)并与 比较使用的字段@EqualsAndHashCode.Include


查看完整回答
反对 回复 2022-07-20
?
蛊毒传说

TA贡献1895条经验 获得超3个赞

当我前一阵子研究这个问题时,我得出结论,没有一个正确的答案。


我最终只检查了and中的@Id属性,因为这似乎表现最好。(我们不使用任何s;它可以代替它,但坚持使用 s 可能更安全。)equals()hashCode()@NaturalId@Id


我认为我发现的唯一潜在问题是,如果一个新实例在被持久化之前被添加到集合中。实际上,这在我们的项目中从未发生过,因此效果很好。(如果在您的项目中确实如此,您可能仍然会发现这是最好的权衡,以避免当持久对象出现在集合中时出现问题,这更常见。)


正如其他答案所指出的那样,如果您覆盖,equals()您还必须覆盖hashCode(),以确保相等的对象始终具有相同的哈希码。(问题的第一个示例确实符合这一点,尽管这两种方法不检查所有相同的字段可能有点令人困惑。)


顺便说一下,在 Kotlin 中,这两种方法变得易于管理:


override fun equals(other: Any?) = other === this

                                || (other is MyEntity && entityId == other.entityId)


override fun hashCode() = entityId

(我为什么喜欢 Kotlin 的另一个例子!)


查看完整回答
反对 回复 2022-07-20
  • 2 回答
  • 0 关注
  • 243 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号