Честный взгляд на Spring Data JPA
«Я всегда прав, на этот раз прав как никогда». 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 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»-разработчики думают, что они не зря писали свои лабы на паскале именно в таком стиле.
Вывод
Паровозик прицепил уже слишком много вагонов и мосты (спины программистов) уже вышли из проектного режима эксплуатации. Надо что-то с этим делать. Давайте думать, _, подсказывайте. Что вы мозги _, подскажите как _ сделать _, по красоте! (Паша Техник)
х/ф Майор Пейн