Как следует использовать равенства и hash-код при использовании JPA и Hibernate
Как следует моделировать equals и hashcode classа в Hibernate? Каковы общие подводные камни? Является ли реализация по умолчанию достаточной для большинства случаев? Есть ли смысл использовать бизнес-ключи?
Мне кажется, что очень сложно правильно работать в любой ситуации, когда учитываются ленивые выборки, генерация id, прокси и т. Д.
- Как реализовать метод hashCode и equals
- Есть ли утилита отражения Java для глубокого сравнения двух объектов?
- Какие проблемы следует учитывать при переопределении равных и hashCode в Java?
- В чем разница между «.equals» и «==»?
- Любые причины, чтобы предпочесть getClass () над instanceof при генерации .equals ()?
- Как проверить, равна ли моя строка нулевой?
- Какова лучшая страtagsя для Equals и GetHashCode?
- Почему мы не можем использовать '==' для сравнения двух чисел с плавающей запятой или двойных чисел
- Hashcode и Equals для Hashset
- BigDecimal равно () по сравнению с compareTo ()
- Сравнивая две строки, игнорируя регистр в c #
- Лучшая реализация для метода hashCode
- Это плохая идея, если equals (null) вместо NullPointerException выбрасывает?
Hibernate имеет приятное и длинное описание того, когда / как переопределить equals()
/ hashCode()
в документации
Суть его в том, что вам нужно только беспокоиться об этом, если ваше лицо будет частью Set
или если вы собираетесь отсоединять / присоединять его экземпляры. Последнее не так распространено. Первый обычно лучше всего обрабатывается через:
- Basing
equals()
/hashCode()
для бизнес-ключа – например, уникальная комбинация атрибутов, которые не будут меняться во время жизни объекта (или, по крайней мере, сеанса). - Если вышеуказанное невозможно, 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 и равны вообще – в зависимости от вашей ситуации.
Как правило, два объекта, загруженные из одного сеанса, будут равны, если они равны в базе данных (без реализации 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 } }