Как должны быть реализованы equals и hashcode при использовании JPA и Hibernate
Как должны быть реализованы равенства и хэш-код класса модели в Hibernate? Каковы типичные ошибки? Является ли реализация по умолчанию достаточно хорошей для большинства случаев? Есть ли смысл использовать бизнес-ключи?
Мне кажется, что довольно сложно заставить его работать в любой ситуации, когда учитываются ленивая выборка, генерация идентификаторов, прокси и т. д.
8 ответов:
Hibernate имеет хорошее и длинное описание того, когда / как переопределить
equals()/hashCode()in документациясуть в том, что вам нужно только беспокоиться об этом, если ваша сущность будет частью
Setили если вы собираетесь отсоединять / присоединять его экземпляры. Последнее не так часто встречается. Первый обычно лучше всего обрабатывается через:
- основывая
equals()/hashCode()на бизнес-ключ-например, уникальная комбинация атрибутов, которые не собирается меняться во время жизни объекта (или, по крайней мере, сеанса).- если это невозможно, база
equals()/hashCode()на первичном ключе, если он установлен и идентификатор объекта /System.identityHashCode()в противном случае. Элемент важно часть здесь заключается в том, что вам нужно перезагрузка Ваш набор после добавления новой сущности и сохранения; в противном случае вы можете столкнуться со странным поведением (в конечном итоге приводящим к ошибкам и / или повреждению данных), потому что ваша сущность может быть выделено ведро, не соответствующее его текущемуhashCode().
Я не думаю, что принятый ответ точный.
чтобы ответить на исходный вопрос:
достаточно ли хороша реализация по умолчанию для большинства случаев?
Да, в большинстве случаев, это.
вам нужно только переопределить
equals()иhashcode()если сущность будет использоваться вSet(что очень часто)и объект будет отсоединен от спящего режима, а затем повторно присоединен к нему сеансы (что является необычным использованием hibernate).принятый ответ показывает, что методы должны быть переопределены, если или условие истинно.
когда сущность загружается через ленивую загрузку, это не экземпляр базового типа, а динамически генерируемый подтип, генерируемый javassist, поэтому проверка того же типа класса завершится неудачей, поэтому не используйте:
if (getClass() != that.getClass()) return false;вместо этого использовать:
if (!(otherObject instanceof Unit)) return false;что также является хорошей практикой, как объяснено на реализация равных в практике Java.
по той же причине, доступ непосредственно к полям, может не работать и возвращать null, а не базовое значение, поэтому не используйте сравнение свойств, а используйте геттеры, поскольку они могут инициировать загрузку базовых значений.
лучшие
equals/hashCodeреализация-это когда вы используете уникальный бизнес-ключ.бизнес-ключ должен быть последовательным по всем сущность состояния (переходный, прикрепленный, отсоединенный, удаленный), поэтому вы не можете полагаться на id для равенства.
другой вариант-переключиться на использование идентификаторы UUID, назначается логикой приложения. Таким образом, вы можете использовать UUID для
equals/hashCodeпотому что идентификатор присваивается до того, как объект будет сброшен.вы даже можете использовать идентификатор объекта для
equalsиhashCode, но это требует, чтобы вы всегда возвращали одно и то жеhashCodeзначение, чтобы убедиться, что значение хэш-кода сущности согласовано во всех переходах состояния сущности. Проверьте этот пост для получения дополнительной информации по этой теме.
Да, это тяжело. В моем проекте equals и hashCode оба полагаются на идентификатор объекта. Проблема этого решения заключается в том, что ни один из них не работает, если объект еще не был сохранен, как идентификатор по базе. В моем случае это терпимо, так как почти во всех случаях объекты сохраняются сразу. Кроме этого, он отлично работает и прост в реализации.
если вам случилось переопределить
equals, убедитесь, что вы выполняете свои контракты: -
- симметричность
- светоотражающая
- транзитивный
- последовательный
- NON NULL
и заменить
hashCode, так как его контракт полагаются наequalsреализация.Джошуа блох (дизайнер Collection framework) настоятельно призвал соблюдать эти правила.
- пункт 9: всегда переопределить хэш-код при переопределении равен
есть серьезный непреднамеренный эффект, когда вы не следуете этим контрактам. Например
List.contains(Object o)может возвращать неправильныйbooleanзначение как не выполненный генеральный контракт.
в документации Hibernate 5.2 говорится, что вы, возможно, не захотите реализовывать хэш - код и равны вообще-в зависимости от вашей ситуации.
Как правило, два объекта, загруженные из одного сеанса, будут равны, если они равны в базе данных (без реализации хэш-кода и equals).
Он получает сложнее, если вы используете два или более сеансов. В этом случае равенство двух объектов зависит от реализации метода equals.
кроме того, вы попадете в беду, если ваш equals-метод сравнивает идентификаторы, которые генерируются только при сохранении объекта в первый раз. Они могут еще не быть там, когда вызывается equals.
здесь очень хорошая статья: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
цитируя важную строку из статьи:
мы рекомендуем реализовать equals () и hashCode () с помощью бизнес-ключа равенство. Равенство бизнес-ключей означает, что метод 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 } }
Comments