Pull to refresh

Comments 31

UFO just landed and posted this here

Улыбнуло про свинью. JPA тем и занимается что автосохраняет все изменения в контексте. Не хотите — detach.

UFO just landed and posted this here
UFO just landed and posted this here
jooq — первая альтернативная технология, с которой у меня есть успешный коммерческий опыт.

jooq оч классная штука и после него вообще не хочется на hibernate/ Spring Data
UFO just landed and posted this here
Когда поверх Нibernate навешивается еще и особенности Spring, то бывают неожиданные чудеса, когда «магия» Spring умножается на «магию» Hibernate.

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


Возможно истинная философия JPA какая-то другая (не могу нагуглить)

Вы передали все верно. Этот анти-паттерн называется "active record".

Я ни разу не видел JPA Entity, спроектированную для наследования.
Хотя возможность наследования сущностей JPA создаёт потенциал для проблем, на практике я с ними не сталкивался.

Сплошь и рядом использую.
Просто для примера, не более:
  • toString в базовом классе (он не JPA, но как пример)
  • Куча таблиц имеющих стандартный шаблон 'ID' поле и 'WTIME' поле (например).


А вообще, лично мне Hibernatе нравится. Но все для своих целей.
Если в проекте активно используется слой логики в встроенных процедурах (типа PL/SQL Oracle), то уж лучше сразу отказаться от Hibernate иначе получается микс.
Если нужно активно забирать из БД данные таблиц/вьюшек, но забирать из них меньшую часть полей, то то же лучше без Hibernate.
Да и вообще, не знаю тонкостей работы Hibernate лучше использовать полный контроль через jdbc. Иначе можно наступить на хитрые грабли (с той же ленивой загрузкой, например)

Как в любом инструменте есть тонкости, плюсы и минусы…
Серебряной пули не бывает.

toString в базовом классе (он не JPA, но как пример) Куча таблиц имеющих стандартный шаблон 'ID' поле и 'WTIME' поле (например)

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

В целом наследование в ООП нужно применять с предельной осторожностью, т.к. несет проблемы, и в 99.99% случаев оно вам не нужно.

Третья проблема из той же серии — вставка новой сущности, которая ссылается на существующую с известным ИДом.

EntityManager.getReference или я не понял проблемы?


Кэшировать JPA сущности нельзя.

Second-Level Cache?


В целом я согласен, JPA хороша для простых приложений. ORM это в целом сложная пролема

Есть ли эта или подобные статьи на английском, коллегам дать почитать?

Не надо писать ерунду. Через entity manager можно добраться до low level API и работать с базой данных через jdbc API. Никто вас ни в чем не ограничивает. После этого утверждения о недостатках перестал читать дальше.

Справедливости ради, стоит уточнить, что не через EntityManager, а через его конкретную имплементацию Session, если говорить о Hibernate. Вот у неё есть замечательный метод.

Я имею ввиду спецификацию jpa. EntitManager в ней — entry point. Кстати, тот кто делал интервью автору статьи тоже далёк от понимания разницы между jpa и Hibernate, иначе он бы спросил о one to many declaration в jpa а не в Hibernate

Несогласен. Не надо личные отношения и кривые руки на всю технологию обопщать
Естественно есть грабли, о который тот же Н.Алименков видео делал. И после этого стал делать видео на тему- как это сделать правильно.

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

select
expr(col1), expr(col2), expr(col3)
from ${schema}.${table}
where ${predicate}

при этом выражения для выборки колонк динамически генерируются, предикат тоже, таблицы перебираются все в пределах схемы, и т.п. Ну т.е. специфичный такой инструмент для определенных целей, который даже bind variables нормально использовать не может по ряду причин. Никаких мыслей применить тут JPA даже не возникало никогда. При этом в других проектах активно применяли, и даже до появления JPA Hibernate 2 пользовались — и никаких таких серьезных проблем не испытывали. Т.е. от типа проекта, от квалификации людей, и много от чего еще все и зависит. А личные вот такие вот отношения очень часто говорят, что человек не разобрался.
Ну как бы -да. ORM — ЗЛО!

Правда я не понял, насчет, того, что сущности не могут быть закрытыми для наследования.
Т.к. в Kotlin я для сущностей использую Data Class'ы.
Так что «если нельзя, но очень хочется — то можно»
Аналогично с LazyInitializationException. Отказываемся от транзакционности (hibernate.enable_lazy_load_no_trans=true) и всё не особо беспокоимся о LazyInitializationException.

Но с основным посылом, что ОРМ создает проблемы, чем их решает — согласен.

Но Spring Data Jpa ну очень удобен. :-)
Т.к. в Kotlin я для сущностей использую Data Class'ы.

А зачем? equals всё равно переопределять, hashCode всё равно переопределять, конструктор без параметров всё равно нужен


Аналогично с LazyInitializationException. Отказываемся от транзакционности

Мне кажется, лучше словить экспешн и поправить код. Дешевле выйдет. ))

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

JPA не пытается. Это разработчики пытаются использовать JPA таким способом и мне кажется большинство экспертов прямо говорят не делать так.


JPA используется для того, чтобы вытащить данные из базы, поправить и скинуть обратно. Или, чтобы просто вытащить. Ещё JPA помогает программисту строить запросы. Ключевое тут помогает. Отдавать построение запросов на откуп JPA — нежелательно.


Конструктор по умолчанию
Классы должны быть открытыми для наследования

Тут я хотел бы сказать, что не нужно пытаться работать со Entity как с объектами. Это не объекты, это структуры. И если пытаться пользоваться одной вещью, так, как будто это другая вещь — ничего хорошего, конечно не выйдет. Наличие конструктора без параметров — для структуры штука закономерная.


Объекты должны быть изменяемыми

Повторюсь, что Entity сделаны для того, чтобы выгрузить данные, поправить и скинуть обратно. Если у вас какой-то другой воркфлоу, то не используйте Entity, JPA в этом случае всё равно будет вполне себе полезной штукой.


Весь код становится кодом с побочными эффектами

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


С ленивой загрузкой надо быть постоянно начеку. Каждый раз, написав что-то в духе entity.getXXXs, задумываться — не случится ли здесь N+1 запрос.

Да, надо. Если обходиться без JPA — придётся писать запрос руками. Если с JPA — придётся руками добавить настройки, чтобы ленивой загрузки не было. Выбор индивидуален, но с JPA работы, наверное меньше. Особенно учитывая, что писать запросы руками JPA не мешает.


Я уверен, что этот список будет и дальше расти. Сейчас я выписал только то, что лежит на поверхности.

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


Получается, что теоретически JPA можно использовать, не жертвуя качеством дизайна и производительностью. Однако придётся пожертвовать идиоматичностью использования JPA.

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


По моему мнению, применение JPA уместно, когда важно сделать быстро, дёшево и плохо.

Если не знаешь, как пользоваться инструментом, всегда получится плохо. Но быстро и дёшево — далеко не всегда )).


Основной недостаток JPA в том, что разработчики не хотят его осваивать )).

Основной недостаток JPA в том, что разработчики не хотят его осваивать )).

Допустим, передо мной выбор: изучить JDBCTemplate, Jooq или JPA. Почему я должен инвестировать свое время именно в JPA?

Почему я должен инвестировать свое время именно в JPA?

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

Т.е. php таки надо тоже было подучить?;)

Если вы джава разработчик, то куда бы ты не пришёл, там с высокой вероятностью будет JPA. Поэтому тем, кто не владеет JPA придётся отклонить бОльшую часть предложений о работе. Если это не проблема, то можно JPA не изучать.


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

Уже минимум лет 10 наверное в сумме разрабатываю на Java без использования JPA и прочих Хибернейтов. Когда же это чудо попадалось (суммарный опыт несколько больше 10), то справлялся с ним не хуже остальных. Поработал в том числе в разных крупных российских компаниях. На текущем месте, например, у нас есть общая рекомендация избегать подобного рода ORM-ов.

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

Про PHP я сказал к тому, что можно с тем же успехом сказать:

Потому, что PHP сейчас практически везде. Куда бы вы не пришли, там с высокой вероятностью где-то будет сайтик на PHP.

Далеко не факт правда, что вам придётся им заниматься). Так-то, куда ни пойди, везде в компаниях ещё 1C есть. Но меня из-за того, что я его не знаю, никто на выход не просит) Да и вообще, про JPA спрашивали вроде только в Люксофте, остальные даже если и используют, то не распрашивают про него на собесах

Любопытно, как на текущем месте работы обоснована рекомендация не использовать JPA. Или это просто так сложилось?

Не возьмусь воспроизвести позицию дословно. Но что-то из этого:

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

Простые вещи делаются примерно так же просто, как и без всей этой мишуры, а сложные даже сложнее. При этом это всё заметно тормозит даже в простейших случаях

Статья автора, не осилившего SELECT NEW и RntityGrpaphs.

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

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


Объекты должны быть изменяемыми

Сделайте сеттеры protected и получите неизменяемую entity. Кроме того, в некоторых JPA объект можно пометить ReadOnly ну или поставить на поле updatable=false.


Весь код становится кодом с побочными эффектами

JPA использует паттерн Active Object. Вся работа с объектами заключена внутри текущего UnitOfWork. Вне его объекты становятся detached но с вполне детерминированным состоянием.


Ленивая загрузка

Это как раз то почему объектная модель плохо совместима с RDB, где все есть таблица. Проблема n+1 запроса — это не проблема JPA как таковой, а вообще всех ORM, причем концептуальная. Тем не менее никто не запрещает добавить JOIN FETCH для массивного запроса. Многие JPA позволяют сократить загрузку с дочерними сущностями до двух запросов, используя для дочернего либо IN(родительские PKs), либо IN(родительский SELECT). В большинстве же ситуаций запросы, которые делает JPA — это загрузить объект по ID, которые выполняются очень быстро.


Дополнительный запрос для обновления сущности

Есть еще кеш и extended persistence context. Но вцелом первый вариант полностью оправдан. При массовом апдейте объектов их лучше сначала вытащить одним запросом. А JPA потом сгенерит один batch update. Кроме того, саму entity можно при желании использовать вкачестве DTO, а для обновления использовать простой merge().


Дополнительный запрос для вставки ссылки

Про EntityManager.getReference(Class<T> entityClass, Object primaryKey); не слышали?


Кэшировать JPA сущности нельзя.

Можно, но осторожно. Есть разные стратегии поведения кеша. Кроме того, при желании можно даже вручную управлять кешем: EntityManagerFactory.getCache().


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


Из реальных проблем JPA отметил бы отсутствие нормального type-safe DSL для запросов (Criteria API это просто ужас), достаточно убогий API и достаточно страшные аннотации для ORM. Поэтому в своих проектах часто приходится многое допиливать и делать различные надстройки и врапперы. Для упрощения разработки могу порекомендовать замечательные библиотеки QueryDSL и JINQ.

Статья должна называться "Почему следует избегать использования JPA/Hibernate в продакшене тем кто не понимает что такое JPA и как работает Hibernate"

Выше про это уже был вопрос. По-моему автор статьи разобрался как работает Hibernate. Вопрос, зачем инвестировать время в его изучение, чтобы, как вы говорите, разобраться? Если есть инструменты, которые решают тот же круг задач проще.

Sign up to leave a comment.

Articles