Pull to refresh

Comments 18

Ваш вариант с hashCode от класса хоть и работает и даже все тесты закрывает, но он будет бесполезен. Все равно, что указать 0 или любое случайное число. Хеш код используется, например, в HashMap, чтобы можно было за почти бесплатно положить его в случайную ячейку памяти и потом за такую же стоимость получить его оттуда. В вашем варианте никаким хешмапам не светит нормальная эффективность, потому что они постоянно будут уходить в linear probing и проверять вообще все элементы на равенство с искомым.

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

Хорошее замечание, меня тоже это во многом смущало, однако в вариантах от Jpa Buddy /Amplicode и джава чемпиона тоже самое)

На это высказался и провел свои замеры Vlad Mihalcea здесь: https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/

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

Если у вас есть вариант получше, я с удовольствием приму его на вооружение.

Ваш Vlad пишет, что вырождённый вариант хэшкода, противоречащий самому смыслу его существования, может использоваться только в случае с JPA, где маленькие коллекции, есть (долгое) чтение из БД, и есть UI, с которым взаимодействует пользователь. И то я не уверен, верно ли это (использование константного хэша) даже в случае с JPA.

Но вы-то берете JPA лишь как пример. Пример, на котором показывается некий общий случай. А в общем случае возвращать константу в качестве хэша это просто бред.

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

Спасибо за ваш отзыв

В начале статьи я подмечаю, что объекты по сути делятся на два типа: модели и сущности. Модели сравниваем по всем полям, они неизменяемые.

И у меня черным по белому(или наоборот 🤔) написано про сравнение сущностей! Касаемо вырожденного варианта по логике это не только для JPA, а для всего, что может генерировать первичный ключ на стороне базы, а это почти внезапно все ORM для всех языков.

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

Да, в идеальном мире у определения сущность, должен быть уникальный идентификатор неизменяемый, но пригенерации в базе это можно достичь только одним путем - натуральный идентификатор. И тут либо JPA и почти все ORM с изащренной идеологией, либо DDD. Тут каждый выбирает для себя сам! Я лишь провел свой анализ и поделился своим мнением.

В целом это то можно избегать (изменяемый id) и использовать либо модели(те же проекции к ним относятся), либо генерацию ключа на стороне приложения и я об этом также написал!

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

Собачки с котиками как раз, чтобы доходчиво показать, что это проблема не только хибера и JPA, а любого сравнения изменяемых объектов!

А кто работает с хибером, все об знают)

Если бы...

Создаётся впечатление, что вы поверхносто пробежали статью. Если это не так, подскажите, что именно вам было не понятно? Или может быть какие-то мои фразы сбили столку?

Да вот именно, что после каких-то собачек много где идут какие-то с воздуха взятые, нетривиальные и не очевидные утверждения, поэтому приходилось просто побегать их, чтобы не зависнуть. Так не пишут. Ну и заголовок - не "на примере JPA", хотя теперь я понимаю, в каком смысле использовалась слово, а просто "в JPA". Далее, я не работал с JPA, но представьте себе кэш на Java, который читает всю таблицу и хранит её в памяти. Использовать в качестве hashCode константу? Бред.

В общем, в самом начале надо было сразу и очень чётко написать - рассмотрим пример, где хэш вычисляется совершенно нелогично и против всех правил. И далее по тексту. Чтобы у читателей было чёткое понимание, что это пример-исключение, а не пример-пример.

Касаемо кэша, кто вообще в эпоху кубера и децентрализованных приложений делает кэш в оперативной памяти для сущностей?)

Да и примеров 7, в 3 из которых нет описанной вами проблемы.

Честно говоря не понимаю ваших возмущений.

Статья начинается со слова "ЛучшИЕ", выбирайте любой по ситуации. Сами ситуации описаны.

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

Я грублю? Ткните пальцем, плиз. Буду знать, на будущее)

очень чётко написать - рассмотрим пример, где хэш вычисляется совершенно нелогично и против всех правил

Вы правы, возможно на грубость и не тянет.

Для меня это воспринимается как: "Лучше бы не писал статью" и возможно это только мои проблемы.

При той же подготовке, вообще нигде нет полного анализа всех вариантов реализации. Что всех, хотя бы основных. Многие пишут абстракто: "Не делай так". А вопрос, как тогда? Описанные выше проблемы магическим образом не решаться, если я не буду писать так)

Поэтому и есть это статья, как попытка показать как надо и где именно.

А вообще, лучшая практика - это не класть новый объект в любой контейнер, основанный на хэш-таблицах, пока он не сохранился в БД и не получил свой Id. Делов-то)

Спасибо за ваш вариант, добавил упоминание о вашей версии.

Здесь я с вами отчасти соглашусь, но тогда связи один ко многим и многие ко многим, к сожалению, превращаются в бойлерплейт код и ограничивают возможности ORM фреймворков.

Проблема настолько стандартна, что не верю, чтобы она уже стандартно и не решалась. А если руками, то не создавайте дочерних объектов, пока не сохраните родительский объект в БД. Опять же, есть много встроенных в БД генераторов id, то есть, id можно получить до сохранения, не используя атрибут autoincrement в базе для ключа. Но это всё равно одно обращение к БД, поэтому я лучше бы просто сохранил новый объект сначала.

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

Это история про компромиссы, нельзя усидеть на всех стульях сразу.

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

Вы возможно и имеете понимание, что серебрянной пули не существует, но имеет ли его типовой разработчик?

Почему должна быть единая точка сохранения? Пока не понимаю.

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

Ну я же написал выше. Сохраните сначала родительскую, потом сколько угодно дочерних в любое время дня и ночи, и даже года.

Самый простой способ работать с equals и hashCode - избегать их по возможности.

Хотите найти объект в списке? Используйте предикат.

Хотите использовать множество? Возможно подойдёт синтетический ключ или, быть может, на самом деле, вы не хотите. Или подойдут деревья с компаратором.

И только если вы действительно проектируете ключи для hashMap или тривиальные сущности с естественным сравнением (какой-нибудь класс Complex или Pair) - тогда да, разумно определить методы equals/hashCode. Но кажется, это не ваш случай.

Этот вариант самый первый в статье со схожим выводом.

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

Также чуть дополню. Когда нам нужно одну огромную коллекцию отфильтровать по другой, можно ещё отдельное множество под айдишники завести и в цикле по сущностям вызывать contains. Сам пользуюсь, кайфую.

Сами сеты на самом деле нормально можно использовать во многом только для многие ко многим и один ко многим, поскольку в том же хибере и ряде ORM есть оптимизация при генерации запросов. Для этих целей сеты даже с вырожденным hashCode прекрасно работают (ибо под капотом там тоже самое дерево получается), сам на работе применяю и все летает, кода мало.

Sign up to leave a comment.

Articles