"Я всегда прав, на этот раз прав как никогда". Linus Torvalds
Стоит сразу сказать, что задача этой статьи не в том чтобы кого-то обидеть, а в том чтобы развенчать миф “нормально делай – нормально будет” в контексте Spring Data JPA. Неконтролируемый паровоз движется в случайном направлении. Можете считать это криком души, моим “хватит”!

@Entity это плохо
Энтити это чудовищное, неполноценное изобретение. Энтити просто не позволяют реализовать все необходимые сценарии. Энтити фиксируют схему, что фактически означает монопольный доступ к СУБД. Кстати, как насчет запроса к промежуточной таблице в случае n:n связи? Чтобы сделать миграции, вам придется использовать sql-мигратор, a.k.a flyway. Также энтити не дадут вам sql-проекций из коробки. Если учесть все эти минусы напрашивается закономерный вопрос, а зачем нам @Entity. Ответ прост, вы добавляете энтити потому что без них не запустится Spring Data JPA.
Сложность получения проекций базы данных
Вообще, думаю мало кто поспорит (пишите в комментарии), что сделать проекцию с базы данных должно быть просто, даже структурированную. Но постойте, вам придется использовать Spring Data JPA! Выбирайте на любой вкус: JPQL (весьма ограниченный), EntityGraph (ну это вообще шутка), Criteria API, программирование на строках в аннотациях Spring, ваше любимое программирование на интерфейсах, программирование на именах методов, возможно еще XML или квери-билдер. Часто это кончается тем, что люди забивают на все это и просто отправляют полный EntityGraph на фронтенд, пример: https://github.com/spring-petclinic/spring-petclinic-rest/tree/master.

Тем временем когда аналитики пишут 100кк проекций в секунду на превосходном SQL с complete набором функций, попивая куба-либре на Мальдивах, нам приходиться с этим возиться.
Невероятное количество документации и технологий
Hibernate это база Spring Data JPA, он один вносит 600+ страниц демеджа документации и миллионы строк кода. Думаю если бы кто-то решил распечатать полную документацию на Spring Data JPA и его друзей, лесам Амазонки пришлось бы тяжко. Функциональность этих библиотек постоянно пересекается, то есть надо тратить мозго-часы на решение задачи выбора. И всё вот это вот ради того чтобы делать тривиальные вещи, как мы сюда попали? Сравните это с JDBC + SQL, на которых можно сделать всё, почему мы так много платим?
Отвратительное дебагабилити
Абсолютно неясно что там Spring сгенерировал и как всё это отлаживать в общем случае. Вся валидация исключительно в Runtime, всё работает на Reflection API. Вставляются специальные костыли чтобы это работало с AOT Java Compiler. Вам нужно прожать все кнопки в приложении чтобы убедиться, что вы где-то неправы, это называется руками жар загребать (стакан водки и за станок).
Названия методов как язык запросов
- Нарушает Java naming convention. (в принципе дальше можно не читать) 
- Write-only. 
- Ограничено одной таблицей. 
- И зачем было начинать? 
Пример в студию (https://vladmihalcea.com/spring-data-query-methods/):
List<PostComment> findAllByPostAndStatusAndReviewLikeAndVotesGreaterThanEqualOrderByCreatedOn(
    Post post,
    PostComment.Status status,
    String reviewPattern,
    int votes
);Автора статьи заботливо указывает:
TL;DR, Don’t write query methods that cannot even fit on the screen.
Лично на мой экран не влезло.
20 лет нерешенных проблем с оптимизацией
Сколько нужно еще времени чтобы решить проблемы с оптимизацией? Может ещё 20? Не говоря о том что у этой штуки жуткий code-base, на самом деле получилось так, что проблемы с оптимизацией решать придется нам, контролировать форму запросов, профиль нагрузки и т.д. Это помимо того, что нам еще и в СУБД эти запросы придется оптимизировать. Умножаем сложность. А вот если просто писать native-query в СУБД, этой проблемы нет. Лично по моему мнению вообще все эти затеи с хранением какого-то состояния базы данных на Java-сервере, это химера, но об этом дальше.

Ортогональность приложения. Контроллер, сервис, репозиторий, дао, энтити, мапстракт
На типовом проекте вам нужно поменять порядка 10 файлов для того чтобы добавить/изменить хоть какую-то функциональность. Не зря люди стараются убежать от этой реальности в low-code решения. Переиспользование энтити в разных контроллерах/сервисах уменьшает ортогональность (single-responsibility). Даже самим java-программистам в лом писать эти груды кода, что приводит к экономии на декомпозиции и появлению вот таких вот карликовых монстров, сервисных классов с 24 методами: https://github.com/spring-petclinic/spring-petclinic-rest/blob/master/src/main/java/org/springframework/samples/petclinic/service/ClinicService.java . И при этом мы, не замечая бабайки, продолжаем рассказывать про SOLID принципы на собеседованиях.
Стейтфул глина. Транзакции
Современное программирование стремится к большей декларативности кода, явности и минимизации количества состояний системы. Здесь же мы получаем полный букет в виде: Entity Life Cycle, Implicit границ транзакций, мутных propagation.requires_new, потенциальных неявных savepoint’ов, detached коллекции, кеша второго уровня (который думает что он один в системе). Видя ваши циклы в которых вы месите коллекции , “Senior”-разработчики думают, что они не зря писали свои лабы на паскале именно в таком стиле.
Вывод
Паровозик прицепил уже слишком много вагонов и мосты (спины программистов) уже вышли из проектного режима эксплуатации. Надо что-то с этим делать. Давайте думать, _, подсказывайте. Что вы мозги _, подскажите как _ сделать _, по красоте! (Паша Техник)

Комментарии (22)
 - Kill_Voice20.11.2024 13:48- Spring Data это реально швейцарский нож, просто проблема, что ножом то как бы тоже можно неправильно пользоваться. Если у вас MSA с компактными сервисами то почему бы и нет, быстро и надежно, если монолит то возможно к использованию есть вопросы, но опять же очень много аспектов, не всё не так однозначно 
 - bugy20.11.2024 13:48- Это старый добрый холивар: ORM vs no ORM? - С очень слабыми аргументами и отсутствием альтернатив. - Я так понимаю, вы против ORM (или hibernate/JPA) в целом, поскольку spring data это всего лишь обертка поверх. Тот же - @Entityвообще в JPA спеке.- Написание SQL ручками? И вы утверждаете, что проблем: - нужно поменять порядка 10 файлов для того чтобы добавить/изменить хоть какую-то функциональность 
- Вся валидация исключительно в Runtime, 
 - с чистым SQL нет? - Если у вас есть 10 запросов, и вы добавили поле, будьте добры все 10 запросов обновить, даже если вы поле всего лишь читаете, чтобы положить в POJO. А если забыли ещё запятую в конце добавить, то о проблеме вы тоже узнаете только в рантайме. - Кстати, как насчет запроса к промежуточной таблице в случае n:n связи? - А как насчёт этого запроса, если у вас промежуточная таблица - другой (микро)сервис? Или такую проблему уже решить нереально? А если реально, то почему такой же подход нельзя использовать для другой таблицы, но в том же приложении? - В общем: "нормально делай, нормально будет" - ПС я главный хейтер спринга в компании. И не то, чтобы фанат hibernate, но как говорится " - ДемократияORM – наихудшая- форма правлениятехнология для работы с БД, если не считать всех остальных." - BugM20.11.2024 13:48- ДемократияORM – наихудшая- форма правлениятехнология для работы с БД, если не считать всех остальных- Jooq же. Отличная промежуточная технология.  - bugy20.11.2024 13:48- Может быть, не работал с ней, не могу дать практический отзыв. - Но как будто бы, количество бойлерплейта там всё равно слишком велико для меня. Но безусловно, она решает многие недостатки sql и выбор между jooq/orm сложнее, чем sql/orm. 
 
 
 - Serge100120.11.2024 13:48- Да что тут думать, hibernate как и spring data jpa НЕ нужны, это лишь дополнительный слой абстракции замедляющий работу. Видимо их используют те, кто не смог в чистый SQL (но как вы тогда собеседование прошли? Задачки по SQL теперь постоянно спрашивают на лайвкодинге) - Пока что лучший вариант для меня - spring jdbcTemplate, где можно писать нативные запросы  - bugy20.11.2024 13:48- Видимо их используют те, кто не смог в чистый SQL - Когда я только начинал карьеру программиста, я слышал похожую фразу: "джаву используют те, кто не сумел в c++". Степень адекватности фразы для меня одинаковая. Продолжая аналогию: "машину используют те, кто не сумел в ходьбу". - Я поработал на продукте, где люди придерживаются вашего мнения и пишут на sql. Проблемы орм мне показались цветочками: - На обновление модели: будь добр обновить все запросы 
- Если ты их не обновил, то когда-нибудь что нибудь на проде не будет работать. Но это не точно 
- Кривые и неоптимальные sql запросы. Потому что это так классно писать сложные запросы в бд, вместо нормальной архитектуры 
  - Serge100120.11.2024 13:48- Продолжая аналогию: "машину используют те, кто не сумел в ходьбу". - Так если бы машину... А то мне тут вместо ходьбы (SQL) предлагают трактор (spring data jpa). Нет уж, я лучше пешочком. - На обновление модели: будь добр обновить все запросы - Кривые и неоптимальные sql запросы. - Так ведь лучше когда сам пишешь, сам оптимизируешь. Если за тебя пишет фреймворк, то потом ещё больше времени на отладку этого всего потратишь, при каждом изменении. - Интересно было бы проверить у тех, кто топит за hibernate/data jpa, как у них со знанием SQL, есть подозрение что люди разучились ручками писать сложные запросы.  - ris58h20.11.2024 13:48- Сложные это какие? И как из одного следует другое? - JPA ограничен в выразительности, отсюда следует, что написать на нём некий произвольный запрос будет сложнее.  - Serge100120.11.2024 13:48- Да хотя бы любой select длиннее пяти строчек. - Знаю некоторых программистов, которые давно разучились и такое писать, поэтому используют/пропагандируют ORM (hibernate/spring data jpa)  - ris58h20.11.2024 13:48- Да хотя бы любой select длиннее пяти строчек. - Конкретный пример, пожалуйста. И пример кода на Spring Data JPA, который бы делал то же самое. Потом оценим насколько легко написать этот код и насколько сложно забыть как это сделать на чистом SQL. 
  - bugy20.11.2024 13:48- SELECT o.id o.creation_date, o.number, o.contractor_id FROM order o WHERE o.id = :user_id- Пойдёт? - Я пропагандирую - repository.findById(id)вместо этих 6 строк.
 
 
 
 
 
 - ALexKud20.11.2024 13:48- Вся и проблема имхо что основной массе нативный SQL использовать сложно ввиду того что мозги там надо поворачивать в сторону алгоритмизации работы с наборами данных а не с линейными алгоритмами обработки на фронтенде для бэкенда. Web испортил многих и кроме трех операторов SQL в ограниченном контексте мало кто знает сам SQL хорошо. Отсюда и растут ноги ORM. Для. Web приложений хватает и ладно. Хуже то что все эти web примочки пацаны тянут в приложения , которые работают в чистом локале, напихивают туда докеры, Джанги и проч., без необходимости, а потом тихо сваливают на сторону с указанием в резюме на "опыт", оставляя другим свои проблемы доработок, зависимостей и рефакторинга. Я имею ввиду не продажные приложения, а внутренний софт технологических и производственных компаний 
 - png20.11.2024 13:48- Посыл правильный - Hiber устарел. ему давно пора на свалку истории. - Но с аргументацией проблемы. - Например, я не понял, чем плох Entity (точнее, я знаю, что там не так, но в статье про это не говорится). - Чем плох flyway? Чем плох рефлекшен и рантайм? проверять качество работы c SQL кроме как в рантайм не где. - Про проблемы работы с вложенными сущностями - ничего нет. - Про проблемы работы с кешем 2 уровня - ничего нет. - Про проблемы работы с java классами - тоже ничего нет. - Дальше, критикуются генерируемые методы в интерфейсах, но это не Hiber и не JPA, это spring data. Это другой фреймворк, на базе его есть Spring Data JDBC или Spring Data Mongo. Ну кто-то написал дикий г-код, мы же не критикуем за это язык программирования? - Дальше, чем не угодили слои приложения. Отделяйте мух от котлет, в хорошей крупной системе слои приложения очень даже нужны. Да, пример из спринга - плохой с точки зрения. ну давайте будем честными, многое в спринге написано отвратительно. Он не ОПП, не SOLID, а лишь имитирует их. Ну и что. Странно... - Итого, чтобы не было недомолвок. Я считаю, что на spring data jpa можно собрать приятный достойный проект. Но очень много старой функциональности, которая уже сильно устарела и надо сильно думать, что из JPA стоит использовать, а что пора выкинуть. Лично мне не нравится JPA с точки зрения парадигмы. На своих проектах, где я влияю на выбор технологий, я стараюсь его не использовать.  - ris58h20.11.2024 13:48- Посыл правильный - Hiber устарел. ему давно пора на свалку истории. - Даже интересно стало на что его нужно по-вашему заменить. Вдруг мне тоже надо. 
 
 - kacetal20.11.2024 13:48- Господи, на что люди готовы пойти лишь бы не использовать jooq. Даже на изучение jpa спецификации и всех неявных подводных камней хибера. - А вообще если время тратить некуда то лучше уж изучить sql и спокойно писать на jooq. 
 - Slobodator20.11.2024 13:48- Ок, короткая шпаргалка, что такое JPA, зачем и как с ним работать. - JPA -- это Java Persistence API, фреймворк, который позволяет мапить Java объекты на реляционную базу данных. - Что это значит на практике? Допустим, у нас есть такой класс (и объекты -- экземпляры этого класса) - public class Order { private Map<Item, Integer> itemsAndTheirAmounts = new HashMap<>(); private Set<PromoCode> promoCodes = new HashSet<>(); private Instant createdAt = Instant.now(); private Money totalPrice = Money.of(0, Monetary.getCurrency("RUB")); }- ... в - Itemи- PromoCode, в свою очередь, наверчено ещё что-то и т.д.- Пока мы работаем с ними в оперативной памяти -- всё ок, у но как только начинаем работать с внешним миром, нужно их как-то различать, для этого добавляется ID. - Entity = доменный класс + ID - Поскольку оперативная память всё ещё ограничена и энергозависима, нужно сохранять (persist-ить) эти объекты где-то, например, в реляционной БД. - Далее, есть такой подход как ООП -- объектно-ориентированное программирование. Одной из его идей является инкапсуляция -- это когда поля закрыты для изменений напрямую, но есть методы, которые гарантируют бизнес-целостность объекта. - В нашем примере это могут быть - addItem(...),- applyPromoCode(...)и т.д., которые изменяют состояние объекта, в том числе и пересчитывая- totalPrice, благодаря чему (и юнит-тестам на них, конечно) мы можем быть уверены, что объект всегда консистентен.- Ещё раз, если у нас есть - addItem(...)и- removeItem(...)с соответсвующими проверками, есть гарантия, что количество товаров положительное, а итоговая цена пересчитана правильно.- Далее, все изменения состояния объекта хорошо бы отобразить обратно в БД. Этим и занимается JPA, что-то вроде - class Service { @Transactional void someMethod(...) { var entity = repo.findById(id); entity.update(...); // нет, repo.save(entity) здесь не нужен } }- Единственный ли это подход? Конечно же нет. - Ещё можно вызывать разные SQL команды, а можно вообще ничего не перегонять на бекэнд, а всё делать сразу в БД с помощью хранимых процедур. У каждого подхода есть свои преимущества и недостатки. - К преимуществам JPA можно отнести, что бизнес-логика пишется на высокоуровневом языке Java, проверяется unit-тестами, а DML операции будут произведены фреймворком. - Ну, а недостатком считается факт, что в частных случаях с помощью SQL можно добиться большей производительности. - Однако, тут есть диллема. Допустим, мы хотим увеличить зарплату (правильнее, конечно, говорить ставку) сотрудников некоторого отдела на 10%. Какой подход лучше - @Transactional void increaseSalary(...) { List<Employee> employees = repo.findAllByDepartment(...); employees.forEach( e -> e.increaseSalaryByPercent(10); ) }- ... т.е. "выгрузить данные из БД, изменить, записать обратно по одному" или bulk update - update emloyees set salary = salary * 1.1 where department_id = :department_id;- ...? - Очевидно, что вторая команда быстрее. - Но кто сказал, что метод - increaseSalaryByPercent(...)настолько прост? Там могут быть (сейчас или добавятся потом) проверки на граничные значения, правила округления и т.д.- Кроме того, написав sql update мы создали вторую точку изменения - salary, теперь придётся всегда об этом помнить и держать их в согласованном состоянии.- Всё рассуждения выше были про изменение состояния объектов, JPA в первую очередь об этом. - Кроме этого обычное занятие приложения это отображать их состояние AKA "читать данные из базы". - С объектной точки зрения корректный путь здесь -- это загружать объекты из базы и преобразовывать их в DTO. - Однако, очевидно, реляционные отношения и SQL предоставляют больше возможности и производительности. Если необходимо этим можно и нужно пользоваться, плата за это -- как в примере выше с bulk update -- поддержание ДВУХ подходов в синхронизированном состоянии. - P.S. Spring PetClinic -- довольно плохой пример использования JPA. Так делать не надо. 
 
           
 

Gremlinquisitor
А в чём посыл статьи? Ну поворчать ТС поворчал. Дальше что? Предложения будут? Кому надо - Query. Кому надо - хранимые процедуры и функции. Но как-то оно с Entity уже привычно и покрывает довольно много задач. Больше бы конкретики к этим "фи", а так какой-то вброс, а не статья.
sshikov
Да просто статья начинается вот с этого. Кем считается, почему считается? Непонятно, откуда взят данный постулат. Это раз.
И второе, наверное даже более важное: а автор вообще швейцарским ножом пытался когда-нибудь делать серьезную работу? Швейцарский нож - это карманный универсальный инструмент, который плохо делает почти все, что умеет. Хлеб им порезать проблематично, пассатижи там если и бывают - то гавно, отвертка - тоже. Поэтому само сравнение не совсем корректное - если предмет данной статьи делает хорошо хоть что-нибудь - то просто определите для себя, в каких задачах вы его употребляете, а для других задач найдите инструмент более подходящий, благо их существует достаточно много. Даже голый JDBC может быть осмысленным вариантом где-то (хотя я лично остановился на Spring Jdbc Template).