Использование автоматически сгенерированного идентификатора объекта hibenate в методах equals и hashcode - PullRequest
4 голосов
/ 28 сентября 2011

Прекрасные равенства и хэш-код, вся теория: здесь , а также здесь

Я принял решение использовать автоматически сгенерированный идентификатор в пределах equals () и hashcode () в ряде моих объектов спящего объекта / домена.

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

Моя точка зрения заключается в том, что в большинстве случаев это гораздо менее вероятно, чем изменение любого другого поля.

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

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

Я что-то упускаю?

Ответы [ 3 ]

3 голосов
/ 28 сентября 2011

Вы должны прочитать Equals and HashCode в вики-сообществе Hibernate.

Основная причина не использовать идентификатор базы данных в equals, и, как следствие, hashCode предназначена дляработа с сохраненными, но не постоянными сущностями.До сохранения все ваши экземпляры будут equal, если только вы не позаботитесь об этом случае явно.

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

1 голос
/ 13 сентября 2013

Нет, я не думаю, что вы упускаете что-то фундаментальное. «Основная причина» проблемы с использованием идентификатора базы данных для equals и hashCode заключается в том, что сгенерированный Hibernate ID хранится в изменяемом поле, и значение этого поля изменяется, когда Hibernate назначает идентификатор. Equals и hashCode должны основываться на неизменяемом состоянии, как описано в разделе «100» * Odersky / Spoon / Venners о написании методов equals / hashCode. Это означает, среди прочего, что вы не можете добавить экземпляр в набор или сравнить его с другим экземпляром до тех пор, пока он не будет сохранен, когда hashCode станет фиксированным.

Единственное, что вам может не хватать, это то, как сложно отслеживать, когда назначается идентификатор, поскольку это делается автоматически в Hibernate. Конечно, вы никогда не могли бы вызвать setId(someNewId), но вы могли бы выполнить запрос, который вызывает сброс сеанса, и целая группа временных объектов, не связанных с запросом, внезапно меняла свои идентификаторы с нуля на не-ноль.

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

James Brundege рекомендует назначить идентификатор в коде приложения, чтобы избежать этой проблемы. У Ланса Арлауса аналогичный подход с использованием фабрики объектов, которая присваивает идентификаторы.

Если вы действительно хотите использовать автоматически сгенерированный идентификатор для equals и hashCode, рассмотрите возможность использования Assert.notNull(id) в ваших реализациях equals и hashCode для обнаружения ошибок кодирования.

См. Также еще один стекопоток вопрос с большим количеством дискуссий о различных подходах.

0 голосов
/ 28 сентября 2011

Я столкнулся с проблемой равенства при автоматической генерации идентификаторов из-за того, что я поместил мои объекты в Set, как только они были созданы на основе нового пользовательского ввода.Таким образом, я не сравнивал их явно, я просто положил их в набор.В тот момент это уже было неудачно, потому что Set полагается на equals () / hashCode () (которые снова были основаны на идентификаторе объекта, потому что у меня не было никакой другой опции).

Затем я изменил его на фактическое созданиеи присвоение идентификаторов себе, что было гораздо лучшим решением.

Пожалуйста, прочитайте эту статью, она точно объясняет проблему: dont-let-hibernate-steal-your-identity

...