Почему equals() у JPA-сущности почти всегда сломан

Вопрос проверяет, понимаете ли вы разницу между identity сущности в памяти и identity в базе. Самый частый вариант в коде — id-based equals со сравнением через getClass().

Проблема первая: у двух transient сущностей id ещё null. Если equals сравнивает id, оба null равны друг другу — два разных объекта окажутся "равны", хотя ни один не сохранён.

Проблема вторая: Hibernate возвращает не сам класс сущности, а proxy-подкласс для lazy-загрузки. getClass() у proxy и у реального объекта — разные классы, даже если id совпадает. Сравнение через instanceof работает, через getClass() ломается молча.

@Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Order other)) { return false; } return id != null && id.equals(other.id); }

Спросят следом: почему нельзя добавить все поля в equals, как предлагает IDE. Ответ — бизнес-поля меняются после сохранения, id стабилен. И почему equals должен возвращать false, если оба id null, а не true, как в наивной реализации через Objects.equals.

equals у сущности — это не про удобство коллекций, это про то, понимаете ли вы жизненный цикл объекта в JPA.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки