Как следует использовать равенства и hash-код при использовании JPA и Hibernate

Как следует моделировать equals и hashcode classа в Hibernate? Каковы общие подводные камни? Является ли реализация по умолчанию достаточной для большинства случаев? Есть ли смысл использовать бизнес-ключи?

Мне кажется, что очень сложно правильно работать в любой ситуации, когда учитываются ленивые выборки, генерация id, прокси и т. Д.

Hibernate имеет приятное и длинное описание того, когда / как переопределить equals() / hashCode() в документации

Суть его в том, что вам нужно только беспокоиться об этом, если ваше лицо будет частью Set или если вы собираетесь отсоединять / присоединять его экземпляры. Последнее не так распространено. Первый обычно лучше всего обрабатывается через:

  1. Basing equals() / hashCode() для бизнес-ключа – например, уникальная комбинация атрибутов, которые не будут меняться во время жизни объекта (или, по крайней мере, сеанса).
  2. Если вышеуказанное невозможно, base equals() / hashCode() для первичного ключа, если он установлен, и идентификатор объекта / System.identityHashCode() противном случае. Важная часть здесь заключается в том, что вам необходимо перезагрузить свой набор после добавления к нему нового объекта и его сохранения; в противном случае вы можете столкнуться с странным поведением (в конечном итоге это приведет к ошибкам и / или повреждению данных), поскольку ваша сущность может быть выделена в ведро, не соответствующее его текущему hashCode() .

Я не думаю, что принятый ответ верен.

Чтобы ответить на исходный вопрос:

Является ли реализация по умолчанию достаточной для большинства случаев?

Ответ – да, в большинстве случаев это так.

Вам нужно только переопределить equals() и hashcode() если сущность будет использоваться в Set (что очень распространено) И сущность будет отделяться от сеансов hibernate и впоследствии повторно присоединяться к ним (что является необычным использованием спящего режима).

Принятый ответ указывает, что методы должны быть переопределены, если любое условие истинно.

Когда объект загружается через ленивую загрузку, это не экземпляр базового типа, но является динамически генерируемым подтипом, сгенерированным javassist, поэтому проверка на том же типе classа завершится неудачно, поэтому не используйте:

 if (getClass() != that.getClass()) return false; 

вместо этого используйте:

 if (!(otherObject instanceof Unit)) return false; 

что также является хорошей практикой, как описано в разделе «Реализация равных в практиках Java» .

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

Лучшая реализация equals / hashCode – это когда вы используете уникальный бизнес-ключ .

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

Другой вариант – переключиться на использование идентификаторов UUID , назначенных логикой приложения. Таким образом, вы можете использовать UUID для equals / hashCode потому что идентификатор назначается до того, как объект очистится.

Вы даже можете использовать идентификатор объекта для equals и hashCode , но для этого требуется, чтобы вы всегда возвращали одно и то же значение hashCode , чтобы убедиться, что значение hashCode объекта согласовано во всех переходах состояния объекта. Проверьте этот пост для получения дополнительной информации по этой теме .

Да, это сложно. В моем проекте equals и hashCode оба полагаются на id объекта. Проблема этого решения заключается в том, что ни один из них не работает, если объект еще не был сохранен, поскольку идентификатор создается базой данных. В моем случае это допустимо, поскольку почти во всех случаях объекты сохраняются сразу. Помимо этого, он отлично работает и его легко реализовать.

Если вам удалось переопределить equals , убедитесь, что вы выполняете свои контракты: –

  • СИММЕТРИЯ
  • REFLECTIVE
  • ПЕРЕХОДНЫЕ
  • CONSISTENT
  • NON NULL

И переопределить hashCode , так как его контракт опирался на equals реализацию.

Джошуа Блох (дизайнер структуры Collection) настоятельно призвал следовать этим правилам.

  • item 9: Всегда переопределять hashCode, когда вы переопределяете equals

Есть серьезные непреднамеренные последствия, когда вы не выполняете эти контракты. Например, List.contains(Object o) может возвращать неправильное boolean значение, поскольку общий контракт не выполняется.

В документации Hibernate 5.2 говорится, что вы, возможно, не захотите реализовать hashCode и равны вообще – в зависимости от вашей ситуации.

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

Как правило, два объекта, загруженные из одного сеанса, будут равны, если они равны в базе данных (без реализации hashCode и equals).

Это осложняется, если вы используете две или более сеансов. В этом случае равенство двух объектов зависит от реализации equal-метода.

Кроме того, вы столкнетесь с проблемой, если ваш метод equals сравнивает идентификаторы, которые генерируются только при сохранении объекта в первый раз. Возможно, они еще не будут там, когда будут называться равные.

Здесь очень хорошая статья: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

Цитируя важную статью из статьи:

Мы рекомендуем использовать equals () и hashCode (), используя равенство Business Key. Равенство бизнес-ключа означает, что метод equals () сравнивает только свойства, которые образуют бизнес-ключ, ключ, который идентифицирует наш экземпляр в реальном мире (естественный ключ кандидата):

Простыми словами

 public class Cat { ... public boolean equals(Object other) { //Basic test / class cast return this.catId==other.catId; } public int hashCode() { int result; return 3*this.catId; //any primenumber } } 
  • String.Equals () не работает по назначению
  • Метод Java - equals в базовом classе и в подclassах
  • Как быстро проверить, имеют ли два объекта передачи данных равные свойства в C #?
  • В чем разница между IEquatable и просто переопределением Object.Equals ()?
  • Использование '==' вместо .equals для строк Java
  • Почему мне нужно переопределить методы equals и hashCode в Java?
  • Есть ли полная ссылка на реализацию IEquatable?
  • Переопределение GetHashCode для изменяемых объектов?
  • Как переопределить метод equals в java
  • Почему нам нужно переопределить метод equals () в Java?
  • Почему эти ==, но не `equals ()`?
  • Interesting Posts

    Аутентификация API Steam

    Android – распаковать папку?

    CSS: центральный элемент в элементе

    Является ли блокнот Windows 7 понятным для разных строк Unix, Mac и Windows?

    Как получить количество строк в JDBC?

    Определить количество строк в текстовом файле

    Как запретить Gson преобразовывать длинное число (строка json) в формат научной нотации?

    Показать дубликаты данных с awk print $ 2, $ 3, $ 4 в одной строке

    Где идут файлы и каталоги, когда я запускаю 'rm -rf folder_or_file_name' в Ubuntu 10.04?

    Вопрос о чистом виртуальном деструкторе

    В Xcode, как подавлять все предупреждения в определенных исходных файлах?

    Изменение размера окон после изменения разрешения экрана

    Использование пакета rJava на Win7 64 бит с R

    Как я могу безопасно закрыть это окно и навсегда избежать появления похожих всплывающих окон от вредоносных программ и шпионских программ от Mackeeper Zeobit?

    Может ли источник питания 20 В 4,51А использоваться с компьютером, для которого требуется источник питания 19 В 3,42 А?

    Давайте будем гением компьютера.