Как в Hibernate должны быть реализованы равенства классов модели и хэш-код? Каковы общие подводные камни? Подходит ли реализация по умолчанию для большинства случаев? Есть ли смысл использовать бизнес-ключи?
Мне кажется, что довольно сложно заставить его правильно работать в любой ситуации, когда учитываются ленивая выборка, генерация идентификатора, прокси и т. Д.
Ответы:
В Hibernate есть хорошее и длинное описание того, когда и как переопределять
equals()
/hashCode()
в документации.Суть в том, что вам нужно беспокоиться только о том, будет ли ваша сущность частью a
Set
или если вы собираетесь отсоединять / присоединять ее экземпляры. Последнее встречается не так часто. С первым обычно лучше всего справиться с помощью:equals()
/hashCode()
на бизнес - ключ - например , уникальная комбинация атрибутов, не собирается менять во время объекта (или, по крайней мере, сессии) жизни.equals()
/hashCode()
на первичный ключ , если он установлен и объект идентичности /System.identityHashCode()
иначе. Важной частью здесь является то , что вам нужно перезагрузить ваш набор после нового объекта было добавлено к нему и сохраняется; в противном случае вы можете столкнуться со странным поведением (что в конечном итоге приведет к ошибкам и / или повреждению данных), потому что ваша сущность может быть размещена в корзине, не соответствующей ее текущему состояниюhashCode()
.источник
refresh()
? Как ваша сущность, которая подчиняетсяSet
контракту, оказывается в неправильном ведре (при условии, что у вас достаточно хорошая реализация хэш-кода).Set.contains(entity)
и вы вернетесьfalse
. То же самое и с get () / put () / etc ...Не думаю, что принятый ответ верен.
Чтобы ответить на исходный вопрос:
Ответ - да, в большинстве случаев это так.
Вам нужно только переопределить,
equals()
иhashcode()
если объект будет использоваться вSet
(что очень часто), И объект будет отсоединен от сеансов гибернации и впоследствии повторно присоединен к ним (что является необычным использованием гибернации).Принятый ответ означает, что методы необходимо переопределить, если выполняется какое- либо из условий.
источник
Лучшее
equals
/hashCode
реализация при использовании уникального бизнес - ключа .Бизнес-ключ должен быть согласованным для всех переходов состояния объекта (временное, присоединенное, отсоединенное, удаленное), поэтому вы не можете полагаться на идентификатор для равенства.
Другой вариант - перейти на использование идентификаторов UUID , назначенных логикой приложения. Таким образом, вы можете использовать UUID для
equals
/,hashCode
потому что идентификатор назначается до того, как объект будет сброшен.Вы даже можете использовать идентификатор объекта для
equals
иhashCode
, но это требует, чтобы вы всегда возвращали одно и то жеhashCode
значение, чтобы убедиться, что значение hashCode объекта согласовано при всех переходах состояния объекта. Прочтите этот пост, чтобы узнать больше по этой теме .источник
BaseEntity
и никогда больше не думайте об этой проблеме. Это занимает немного места на стороне db, но эту цену лучше заплатить за комфорт :)Когда сущность загружается посредством отложенной загрузки, это не экземпляр базового типа, а динамически сгенерированный подтип, созданный javassist, поэтому проверка того же типа класса завершится ошибкой, поэтому не используйте:
вместо этого используйте:
что также является хорошей практикой, как описано в разделе «Реализация равенства в практиках Java» .
по той же причине прямой доступ к полям может не работать и возвращать null вместо базового значения, поэтому не используйте сравнение свойств, а используйте геттеры, поскольку они могут запускать загрузку базовых значений.
источник
Да, это сложно. В моем проекте equals и hashCode полагаются на идентификатор объекта. Проблема этого решения в том, что ни один из них не работает, если объект еще не сохранен, поскольку идентификатор генерируется базой данных. В моем случае это терпимо, поскольку почти во всех случаях объекты сохраняются сразу. Помимо этого, он отлично работает и прост в реализации.
источник
В документации Hibernate 5.2 говорится, что вы, возможно, вообще не захотите реализовывать hashCode и equals - в зависимости от вашей ситуации.
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
Как правило, два объекта, загруженные из одного сеанса, будут равны, если они равны в базе данных (без реализации hashCode и equals).
Это усложняется, если вы используете два или более сеанса. В этом случае равенство двух объектов зависит от вашей реализации метода equals.
Кроме того, у вас возникнут проблемы, если ваш метод equals сравнивает идентификаторы, которые генерируются только при сохранении объекта в первый раз. Их может еще не быть, когда призовут равных.
источник
Здесь очень хорошая статья: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
Цитируя важную строчку из статьи:
Проще говоря
источник
Если вам довелось переопределить
equals
, убедитесь, что вы выполнили его контракты: -И переопределить
hashCode
, поскольку его контракт зависит отequals
реализации.Джошуа Блох (дизайнер фреймворка Collection) настоятельно призывал следовать этим правилам.
Несоблюдение этих договоров чревато серьезными непредвиденными последствиями. Например,
List#contains(Object o)
может вернуть неправильноеboolean
значение, поскольку общий договор не выполнен.источник