Как правильно переопределить равно для объекта Hibernate с @NaturalId - PullRequest
0 голосов
/ 04 марта 2019

Много раз уже обсуждалось, как переопределить equals / hashCode для Entity.

Мой вопрос о необходимости использовать все поля в равных.Рассмотрим два случая.

Когда мы используем все поля для равных:

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

Как мы видим, теперь обе сущности будут равны.

Мой вопрос заключается в том, должна ли одна и та же сущность быть равной при переходе к управлению состоянием или нет.И соответственно, как правильно переопределить равенства в этой ситуации.

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Когда я изучил это некоторое время назад, я пришел к выводу, что единого правильного ответа не существует.

Я закончил проверкой только свойства @Id в equals() и hashCode(), поскольку это, казалось, вело себя лучше всего.(Мы не используем @NaturalId s; вместо этого он может работать с этим, но может быть безопаснее придерживаться @Id.)

Я думаю, что единственная потенциальная проблема, с которой я столкнулся, былаесли новый экземпляр добавляется в коллекцию перед сохранением.На практике этого никогда не происходит в нашем проекте, и поэтому он работает хорошо.(Если это произойдет в вашем проекте, вы все равно можете найти это лучшим компромиссом, чтобы избежать проблем, когда постоянные объекты появляются в коллекциях, что гораздо чаще встречается.)

Как и другиеответы указали, что если вы переопределяете equals(), вы должны также переопределить hashCode(), чтобы гарантировать, что равные объекты всегда имеют одинаковый хэш-код.(Первый пример вопроса соответствует этому, хотя, возможно, немного непонятно, как два метода не проверяют все одинаковые поля.)

Кстати, в Kotlin эти два метода становятся незначительно управляемыми:

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

override fun hashCode() = entityId

(Еще один пример того, почему я люблю Котлина!)

0 голосов
/ 04 марта 2019

Согласно эта статья equals и hashCode должна быть независимой от состояния.Если вы переопределили только первое, это плохо и может вызвать странные ошибки.Они должны иметь контракт .

Самый простой способ - использовать ломбок - аннотируйте свой класс с помощью @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) и полей, используемых по сравнению с @EqualsAndHashCode.Include.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...