Как должны быть реализованы equals и hashcode при использовании JPA и Hibernate

StackOverflow https://stackoverflow.com/questions/1638723

Вопрос

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

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

Это было полезно?

Решение

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

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

<Ол>
  • На основе equals () / hashCode () для бизнес-ключа - например, уникальная комбинация атрибутов, которая не будет изменяться в течение времени жизни объекта (или, по крайней мере, сеанса).
  • Если вышеприведенное невозможно, base equals () / hashCode () для первичного ключа, если оно установлено, и идентификатор объекта / System.identityHashCode () иначе. важный раздел здесь заключается в том, что вам нужно перезагрузить свой Сет после добавления в него новой сущности и ее сохранения; в противном случае вы можете столкнуться со странным поведением (в конечном итоге приводящим к ошибкам и / или повреждению данных), поскольку ваша сущность может быть выделена в корзину, не соответствующую ее текущему hashCode () .
  • Другие советы

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

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

      

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

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

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

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

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

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

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

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

    , что также является хорошей практикой, как объясняется на Внедрении равных в практиках Java .

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

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

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

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

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

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

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

    • СИММЕТРИЯ
    • РЕФЛЕКСИВНЫЙ
    • ТРАНЗИТИВНЫЙ
    • ПОСЛЕДОВАТЕЛЬНЫЙ
    • НЕНУЛЕВОЙ

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

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

    • пункт 9 повестки дня:Всегда переопределяйте хэш-код, когда вы переопределяете 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).

    Это становится сложным, если вы используете два или более сеансов. В этом случае равенство двух объектов зависит от реализации метода 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 
    }
    
    }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top