猿问

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

已经多次讨论过如何为实体重新定义 equals/hashCode。


我的问题是关于是否需要使用 equals 中的所有字段。考虑两种情况。


当我们将所有字段用于等于时:


@Entity

public 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 时:


@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(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

    }

}

正如我们所看到的,现在两个实体将是平等的。


我的问题是同一实体在过渡到管理状态时是否应该平等。因此,在这种情况下如何正确地重新定义等号。


慕工程0101907
浏览 244回答 2
2回答

三国纷争

根据这篇文章 equals,hashCode应该与状态无关。如果你只覆盖了第一个,那就不好了,可能会导致奇怪的错误。他们需要有一份合同。最简单的方法是使用 lombok - 用 注释您的类,@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)并与 比较使用的字段@EqualsAndHashCode.Include。

蛊毒传说

当我前一阵子研究这个问题时,我得出结论,没有一个正确的答案。我最终只检查了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 的另一个例子!)
随时随地看视频慕课网APP

相关分类

Java
我要回答