简体   繁体   English

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

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

Many times already discussed how to redefine equals/hashCode for Entity. 已经多次讨论了如何为实体重新定义equals / hashCode。

My question is about the need to use all the fields in equals. 我的问题是需要在等号中使用所有字段。 Consider two cases. 考虑两种情况。

When we use all fields for 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);
    }
}

And test: 并测试:

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

As we see, when translating entities from a transient into a manage state - the same entities will not be equal. 如我们所见,当将实体从暂态转换为管理状态时,相同的实体将不相等。

Another case is when we use in equals only @NaturalId: 另一种情况是当我们仅使用等于@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);
}

And test: 并测试:

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

As we see, now both entities will be equal. 如我们所见,现在两个实体将相等。

My question is whether the same entity should be equal in the transition to manage state or not. 我的问题是,在过渡到管理状态时,同一实体是否应该相等。 And accordingly how to correctly redefine the equals in this situation. 因此,在这种情况下,如何正确地重新定义平等。

According to this article equals and hashCode should be state-agnostic. 根据这篇文章, equalshashCode应该是不可知的。 If you have overriden only the first one, it's bad and can cause strange bugs. 如果仅覆盖第一个,则不好,并且可能导致奇怪的错误。 They need to have a contract . 他们需要签订合同

The simplest way would be to use lombok - annotate your class with @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) and the fields used in comparison with @EqualsAndHashCode.Include . 最简单的方法是使用@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)使用@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)和与@EqualsAndHashCode.Include比较的字段对类进行@EqualsAndHashCode.Include

When I looked into this a while ago, I concluded that there's no single right answer. 当我前一段时间对此进行研究时,我得出结论,没有单一的正确答案。

I ended up checking only the @Id property in equals() and hashCode() , as that seemed to behave best. 我最终检查了equals()hashCode()@Id属性,因为这似乎表现最好。 (We don't use any @NaturalId s; it could work with that instead, but it may be safer to stick with the @Id .) (我们不使用任何@NaturalId ;它可以代替它使用,但是坚持使用@Id可能更安全。)

I think the only potential issue I found with that was if a new instance gets added to a collection before being persisted. 我认为,我发现的唯一潜在问题是,新实例在持久化之前是否已添加到集合中。 In practice, that never happens in our project, and so it works well. 实际上,这在我们的项目中从未发生过,因此效果很好。 (If it does in your project, you may still find this the best trade-off, to avoid problems when persisted objects appear in collections, which is much more common.) (如果在您的项目中这样做,您可能仍会发现这是最佳的折衷方案,以避免持久化对象出现在集合中时出现问题,这种情况更为常见。)

As other answers have pointed out, if you override equals() you must also override hashCode() , to ensure that equal objects always have the same hashcode. 正如其他答案所指出的那样,如果重写equals()必须重写hashCode() ,以确保相等的对象始终具有相同的哈希码。 (The question's first example does comply with this, though it's perhaps a little confusing for the two methods not to check all the same fields.) (问题的第一个示例确实符合此要求,尽管这两种方法不检查所有相同字段可能会有些混乱。)

In Kotlin, by the way, those two methods become manageably small: 顺便说一句,在科特林,这两种方法变得可以管理的很小:

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

override fun hashCode() = entityId

(Yet another example of why I love Kotlin!) (还有另一个为什么我爱科特林的例子!)

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

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