Pull to refresh

Comments 24

Это все конечно замечательно, синтаксический сахар и все вот это вот удобство.. но вы же, наверно, вкурсе о больших накладных расходах при создании record-классов? Нет? Проведите стресс тест системы, будете удивлены. Крайне не советую в принципе использовать в высоконагруженной системе все эти удобства.

о больших накладных расходах при создании record-классов

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

Увы, материалы сам когда-то искал, так и не нашел. Провел пару тестов в нагрузке (tcp - 10к клиентов с рейтом в 30-40rps) - профайлер показал деградацию в Record<init> (jdk 19).

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

Да, имхо единичный "мой" случай, дособираю статистику и вышлю багрепортом.

> вы же, наверно, вкурсе о больших накладных расходах при создании record-классов
Больших расходов по сравнению с чем? С созданием обычных объектов, или не пулом объектов?

Ох уж эти Spring Java Developer-ы...

Такая организация кода - говнокод, хоть с записями, хоть с обыкновенными классами. Транзакции должны управляться на уровне инфраструктурных фасадов, а не репозиториев.

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

Транзакции должны управляться на уровне инфраструктурных фасадов, а не репозиториев.

А в документации пишут "CRUD methods on repository instances are transactional by default."


открывается 2 транзакции, между которыми

А разве сам метод не будет выполняться одной транзакцией (с вложенными из репозитория)?

транзакций врядли 2 откроется, если не переопределять propagation, но думаю, лучше просто использовать @Transactional(readOnly = true) над сервисами для get, а не над репозиториями, потому что если эти методы репозиториев будут переиспользовать для update методов, то сущность не проапдейтится

а не над репозиториями

Цитата:


By default, CRUD methods on repository instances inherited from SimpleJpaRepository are transactional. For read operations, the transaction configuration readOnly flag is set to true. All others are configured with a plain @Transactional so that default transaction configuration applies.

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions


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

Как так? Помечать RO методы, которые делают апдейт? О_о

у тебя есть метод с readonly, возвращающий сущность Optional<Tag> getTagById(long id); https://github.com/isdn/records-dto/blob/01c1cd4c298499f250cb2b7113d91745ad47c6bf/src/main/java/dev/isdn/demo/records_dto/app/domain/tag/TagRepository.java#L28 и ты его используешь только в update https://github.com/isdn/records-dto/blob/01c1cd4c298499f250cb2b7113d91745ad47c6bf/src/main/java/dev/isdn/demo/records_dto/app/domain/tag/TagService.java#L43

При readonly dirty-check не должен работать https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/

тебя спасло только, что в сервисе над update висит обычный не readonly transactional. Отсюда вопрос, зачем над getTagById висит readonly, если можно сразу над сервисом разруливать транзакцию

и ты его используешь только в update

Не только. См. код.


При readonly dirty-check не должен работать https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/

Цитата:


Prior to Spring 5.1, when using Hibernate, the readOnly attribute of the @Transactional annotation was only setting the current Session flush mode to FlushType.MANUAL, therefore disabling the automatic dirty checking mechanism.
However, because the readOnly attribute did not propagate to the underlying Hibernate Session, I decided to create the SPR-16956 issue and provided a Pull Request as well, which after being Jürgenized, it got integrated, and available starting with Spring Framework 5.1.

В pom.xml версия Spring Boot 2.7.4, в которой Spring Framework 5.3.23.


тебя спасло только, что в сервисе над update висит обычный не readonly transactional.

Это сделано намеренно.


Отсюда вопрос, зачем над getTagById висит readonly, если можно сразу над сервисом разруливать транзакцию

Да в принципе можно было бы и убрать, т.к. там по дефолту readOnly. Просто для наглядности, это все-таки демонстрационный проект.
К тому же, getTagById используется еще и в других местах.

Меня всегда удивляет боязнь девелоперов вместо DTO отдавать сразу Entities. Начинают что-то говорить, что это неправильно, и что-то про разные модели и секьюрити. Но на практике это те же самые POJO, и в 99% случаев из fieldset-ы совпадают. А при грамотно выставленных EntityGraphs можно четко контролировать отдаваемый контент. Поэтому чаще всего следуя каким-то искусственным паттернам, сами себе усложняем жизнь.

Ну не знаю. Все-таки у многих объектов есть дополнительные филды, типа created, updated, author и т. д., которые нужны в работе, но не нужны фронтенду, и DTO сильно помогает. Ну и плюс сразу защищаешься от сложностей при переименовывании полей. Удобно, как мне кажется.

Для сильно служебных полей есть @JsonIgnore и ещё куча способов исключить их из сериализации. Хорошая модель данных организована так, чтобы отделить процессинг от стейта: не загромождать сами Entities промежуточными состояниями и служебными данными, а выделить их в отдельный объект.

Согласно моему горькому опыту в любом проекте очень быстро замусоривается DTO, всевозможными мепперами и промежуточными сервисами. Доходит до того, что на одну entity получается с десяток схожих DTO. В то же время успешный рефакторинг на раздачу Entities уменьшил кодовую базу сразу на 90%.

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

Даже не могу представить зачем при одном API может понадобиться десяток схожих DTO. Я могу понять два схожих DTO: для запроса и для ответа. Но зачем еще куча?

Ну и конечно не представляю, что там за код такой был, на 90 % состоящий из мапперов и DTO. Взглянуть бы на проект до рефакторинга.

Там до Api как такового никому особо дела не было. Проект прошел по куче рук, и каждый реализовал тикет как мог. Для каждой новой фичи на бэке тупо писался свой контроллер, сервис, DTO и маппер. В итоге все т.н. "api" -- это куча findByXxx, которые отдают схожие DTO с немного разным набором филдов под каждый конкретный кейс.

Ну так тут очевидно проблема была совсем не в паттерне DTO как таковом.

Это понятно. Просто изначально вопрос был, если модели данных практически полностью совпадает с API, то зачем делать ещё один слой, вместо того, чтобы отдать сразу Entities? Пример из прошлого: инвентарная система с огромным количеством разных Entities и API для доступа ко всем ним.

Пример из прошлого: инвентарная система с огромным количеством разных Entities и API для доступа ко всем ним.

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

при грамотно выставленных EntityGraphs

Я тебе один умный вещь скажу, только ты не обижайся. (с) Не везде используется hibernate. Во-вторых, EntityGraphs, емнип, не позволяют исключить из загрузки "примитивные" типы данных, не вложенные сущности. Возьмём наипростейший пример с пользователем и его банковским счётом. У счёта, как правило, куча атрибутов, а на форме оплаты из личного кабинета - тупой комбобокс с выбором номера счёта, с которого оплату следует списать. И вот вопрос, а зачем мне для отображения грузить из БД всю трехомудь, если надо-то только номер и идентификатор?

EntityGraphs, емнип, не позволяют исключить из загрузки "примитивные" типы данных,

Может, и уже достаточно давно.

Возьмём наипростейший пример с пользователем и его банковским счётом

Это понятно. Но это не единственный кейс, хоть и распространенный. Если у вас инвентарная система с сотнями сущностей, и нужно организовать универсальный доступ к этим данным, при этом основной потребитель даже не фронт, а другие сервисы... задолбаетесь строчить тонны ненужных DTO и маппингов. Проще выставить всю модель наружу, при этом контролируя доступ к определенным полям. В качестве референтов можно указать Hateos и JPARS.

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

Sign up to leave a comment.

Articles