繁体   English   中英

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

[英]How to correctly override equals for Hibernate entity with @NaturalId

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

我的问题是需要在等号中使用所有字段。 考虑两种情况。

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

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

如我们所见,现在两个实体将相等。

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

根据这篇文章, equalshashCode应该是不可知的。 如果仅覆盖第一个,则不好,并且可能导致奇怪的错误。 他们需要签订合同

最简单的方法是使用@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)使用@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)和与@EqualsAndHashCode.Include比较的字段对类进行@EqualsAndHashCode.Include

当我前一段时间对此进行研究时,我得出结论,没有单一的正确答案。

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

我认为,我发现的唯一潜在问题是,新实例在持久化之前是否已添加到集合中。 实际上,这在我们的项目中从未发生过,因此效果很好。 (如果在您的项目中这样做,您可能仍会发现这是最佳的折衷方案,以避免持久化对象出现在集合中时出现问题,这种情况更为常见。)

正如其他答案所指出的那样,如果重写equals()必须重写hashCode() ,以确保相等的对象始终具有相同的哈希码。 (问题的第一个示例确实符合此要求,尽管这两种方法不检查所有相同字段可能会有些混乱。)

顺便说一句,在科特林,这两种方法变得可以管理的很小:

override fun equals(other: Any?) = other === this
                                || (other is MyEntity && entityId == other.entityId)

override fun hashCode() = entityId

(还有另一个为什么我爱科特林的例子!)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM