Следует ли рассматривать поле id объекта JPA в равных и hashCode? - PullRequest
15 голосов
/ 08 сентября 2011

Я столкнулся с проблемой при написании тестов для приложения базы данных с использованием JPA2 и EclipseLink:

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

Сначала я написал что-то вроде

assertEquals(expResult, dbResult);

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

Я вижу два варианта:

  • Либо я удаляю поле id из equals и hashCode, чтобы сравнение основывалось только на "реальных значениях". Я не знаю, вызывает ли это проблемы в базе данных или где-то еще.

  • Или я пишу свои тесты для явной проверки каждого поля, кроме id вручную.

Что мне делать?

Ответы [ 4 ]

15 голосов
/ 08 сентября 2011

Вы можете найти много споров об этом. Моя позиция заключается в том, что вы абсолютно не используете первичный ключ базы данных для чего-либо в вашем приложении. Это должно быть полностью невидимым. Идентифицируйте ваши объекты в вашем приложении по другому свойству или комбинации свойств.

Что касается «тестирования операций сохранения», то вам действительно нужно проверить, правильно ли были сохранены и загружены поля и, возможно, первичному ключу присвоено какое-то значение при его сохранении. Вероятно, это совсем не работа для метода equals.

8 голосов
/ 08 сентября 2011

Использование идентификаторов базы данных в вашей реализации equals и hashCode не рекомендуется.Вы должны полагаться на действительно уникальные / полу-уникальные атрибуты ваших классов при проверке на равенство и при генерации значений хеш-кода.Документация Hibernate содержит обширную страницу, на которой обсуждается это, и приведенные факты применимы более или менее к каждому провайдеру JPA.

Основная причина использования бизнес-ключей для значений, генерируемых базой данных, в вашихРеализация equals и hashCode заключается в том, что поставщик JPA должен фактически выдать SELECT после сохранения сущности в базе данных.Если вы сравниваете объекты, используя идентификаторы, сгенерированные базой данных, то в конечном итоге вы получите тест на равенство, который не пройден в следующих сценариях:

  • Если E1 и E2 являются объектами класса E (чтопроверяет равенство, используя идентификаторы, сгенерированные базой данных), тогда, если E1 и E2 будут равны, если они еще не сохранены в базе данных.Это не то, что вам нужно, особенно если вы хотите сохранить E1 и E2 в некоторых Set до сохранения.Это хуже, если атрибуты E1 и E2 имеют разные значения;реализация equals предотвратит добавление двух существенно разных объектов в Set, а реализация hashCode даст вам O(n) время поиска, когда объекты ищутся из HashMap с использованием первичного ключа.
  • Если E1 - это управляемый объект, который был сохранен, а E2 - это объект, который не был сохранен, то тест на равенство сочтет, что E1! = E2 в сценариигде все значения атрибутов E1 и E2 (кроме идентификаторов) аналогичны.Опять же, это, вероятно, не то, что вам нужно, особенно если вы хотите избежать дублирования сущностей в базе данных, которые отличаются только идентификаторами, сгенерированными в их базе данных.

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

7 голосов
/ 08 сентября 2011

Из книги Hibernate в действии рекомендуется определить бизнес-ключ и проверить его на равенство.Бизнес-ключ - это «свойство или некоторая комбинация свойств, уникальная для каждого экземпляра с одинаковой идентификацией базы данных».В других областях говорится, что не следует использовать идентификатор в качестве одного из этих свойств и не использовать значения в коллекциях.

1 голос
/ 08 сентября 2011

Я бы написал свой тест для явной проверки полей.Чтобы упростить это, перед выполнением теста assertEqual я установлю идентификатор ожидаемого и фактического результата в одно и то же предопределенное значение, а затем использую обычный метод equals.

Удаление идентификатора из equals неоправданно,только потому, что тестирование немного сложнее.Вы получаете серьезные преимущества производительности, а также целостность кода.

...