Как стать автором
Обновить

Комментарии 24

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

Отказ от jpa ничего не поменяет, для изменений надо менять квалификацию разработчиков

jooq — первая альтернативная технология, с которой у меня есть успешный коммерческий опыт.

jooq оч классная штука и после него вообще не хочется на hibernate/ Spring Data
Spring Data JPA пытается решать какие-то из перечисленных проблем, например c Projection. На хабре уже были статьи по тому как избавиться от некоторых стандартных проблем, но соглашусь статья актуальна. Бездумное использование фреймворка приводит к большим проблемам, видел примеры как в одном приложении одним запросом вся база данных вытагивалась, просто потому что не было понимания что такое EagerLoading.
Когда поверх Нibernate навешивается еще и особенности Spring, то бывают неожиданные чудеса, когда «магия» Spring умножается на «магию» Hibernate.

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


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

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

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

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


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

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

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

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.

Статья автора, не осилившего 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"

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.