Comments 17
Лучший способ - не использовать @Transactional
Почему?
потомучто консистентность для слабаков
Я думаю, что изначально комментатор имел ввиду, что при работе с такой важной вещью как транзакционность, лучше полагаться на более очевидный и понятный всем код, нежели чем на AOP магию. Не зная особенностей работы последней, можно очень здорово стрельнуть себе в ногу голову, особенно в каком-нибудь финтехе.
В каком-нибудь финтехе все в курсе как что работает
Очень ошибочное мнение. Я встречал финтехи где повесили Transactional на parent класс контроллера и со спокойной душой пошли в прод. Ну а когда пришли веселые времена, унесли ноги.
более очевидный и понятный всем код
Это тот код, который закреплен в гайдлайнах проекта. Аннотации - значит аннотации. Шаманские камлания - значит шаманские камлания.
Ну и называть АОП магией в то время, как AspectJ в прошлом году отпраздновал 20-летний юбилей - ну это такое. Приемы АОП давно уже в джентльменском наборе любого разраба на JVM.
Есть принципиальный момент в том, что спринг для своей магии компоненты оборачивает в динамическую прокси из стандартной джава библиотеки, в которой не работает обычное наследование. Поэтому если в одном сервис классе публичные транзакционные методы вызывают друг друга, то аннотация может неявно неотработать. Явно вызывать TransactionTemplate от spring будет надёжней. Но для простых crud сервисов удобней использовать аннотацию из статьи.
Также в spring есть тренд в отказе от аннотаций в пользу функционального декларативного стиля в веб модуле.
Это зависит от того, какой стек используем. Если вся логика лежит в БД на уровне хранимых процедур (старый финтех), то да, можно и лучше вообще не использовать, база сделает все сама. При наличии любой ОРМ уже так просто не отделаться. И если выбирать между аннотацией и явным созданием транзакции через sessonFactory - лучше аннотация. код становится сильно проще. Что, впрочем, не отменяет необходимости знать, что за магия там творится под капотом, чтобы не стрелять себе в ногу
Размещать управление транзакциями на сервисном слое, как мне кажется, характерная черта именно simple domain model. Что касается rich domain model, то почему бы не разместить слой транзакций в контроллерах?
Ничего не мешает работать с rich model на уровне сервиса. Контроллер не вмещает в себя функцию бизнес логики - его задача получить данные из сервиса и отдать клиенту, максимум - преобразовать в ДТО. Но консистентносить и наполненность должна обеспечиваться именно сервисом
Я пробовал подход с использованием rich domain model и размещением слоя транзакций исключительно в сервисах. По итогу большая часть сервисов выступала просто обёрткой над моделью, т.е. каждый метод такого сервиса просто вызовал какой-то один метод модели и помечался анотацией Transactional. Собственно все. Мне кажется, что в таких случаях сервисы - это лишние сущности.
Это зависит от размеров и сложности сервиса. Если у вас пара контроллеров да пара сущностей - то да, созданием прослоек можно пренебречь, но если приложение крупное, то создание этих оберток обосновано. В крупном приложении нужно строже придерживаться целевого назначения компонентов. То, что сейчас у вас всего 1 вызов в сервисе и сразу его возврат в контроллер, не значит что при решении новой задачи не появиться необходимость 2 вызовов, где транзакция нужна только для одного и тут внезапно придется вписывать слой сервисов. А вписывать его нужно целиком, чтобы не нарушать единообразия, а значит в какой то момент времени в ПР вместо добавления одного метода внезапно придется писать кучу вот таки пробрасывающих объектов, чтобы сохранить стилистику кода приложения. А при разработке приложения чуть сложнее, чем предоставление данных из BD в UI as is, такая необходимость возникнет гарантированно, а значит и сервисный слой будут закладывать еще на этапе проектирования архитектуры, даже если в первые пол года при разработке прототипа он окажется избыточным.
Цель разбиения на слои не только в технической необходимости, но еще и в соблюдении единообразия проекта, чистоты кода, проектного code style, снижения сложности входа в проект новых разработчиков и т.д. Собственно это базовые принципы использования любого архитектурного паттерна с заявкой на то, что сервис будет функционировать и эксплуатироваться.
"То, что сейчас у вас всего 1 вызов в сервисе и сразу его возврат в контроллер, не значит что при решении новой задачи не появиться необходимость 2 вызовов, где транзакция нужна только для одного и тут внезапно придется вписывать слой сервисов. " - мне кажется имеет смысл, если метод не требующий транзакции выполняется достаточно долго, чтобы оказывать заметное влияние на время выполнения транзакции. Но честно говоря, я такие случаи встречал очень редко.
"Цель разбиения на слои не только в технической необходимости " - с этим никто и не спорит.
Не используйте ее на веб-уровне, поскольку это может увеличить время отклика транзакции базы данных и усложнить предоставление правильного сообщения об ошибке для определенной ситуации (например, согласованность, дедлок, получение блокировки, оптимистическая блокировка).
А тут можно подробнее? Время отклика увеличивается за счёт сериализации, проверки прав? Это критично? Каким образом усложняет я обработка ошибок?
Спрашиваю, потому что не везде именно такая слоеная архитектура, и не всегда хочется тянуть spring на операционный уровень. Тогда возникает хороший вопрос, где следует ставить такие аннотации?
Чем шире scope конкретной транзакции, тем дольше она выполняется. Особенно если внутри нее есть еще запросы в другие сервисы / базы. Во время того, как транзакция открыта, соединение с БД не может быть возвращено назад в пул и использовано другими запросами. Рано или поздно упретесь в лимит открытых конекшенов. Если в рамках транзакции вы делаете лок (select for update) и держите ее открытой дольше чем необходимо для обновления стэйта сущности, которую лочите, то другие ресурсы тоже не смогут получить к ней доступ на запись и будут скапливаться в очередь, что может привести к приличной деградации. Ну и бонусом, всякие неприятные последствия для бизнес логики, если ваша транзакция откатится из-за какого-нибудь минорного исключения выше по стэку, который на суть бизнес операции не влияет никак.
В целом, для небольших проектов не под нагрузкой все эти моменты едва-ли будут ощутимы. Однако это очень частая проблема при росте. И большой вопрос, стоит ли такую бомбу замедленного действия закладывать со старта.
Лучший способ использовать аннотацию Spring Transactional