Пару лет назад попался обзор на Зевса, там домики строились буквой Г. После чего я прошёл все компании на максимальной сложности. Одна из любимых игр детства.
Ага, можно. Проблема в том, что если у тебя сложный запрос, использующий несколько @Entity, то в чьем репозитории его хранить? Вторая проблема, если пихать "одноразовые" методы c @Query в репозиторий, то репозиторий пухнет. Если таких методов немного - @Query будет отличным вариантом.
У вас был вариант писать в нужных местах native query через entity manager. Почему выбрали долгий путь и переписали весь проект на альтернативную технологию вместо этого варианта?
У вас теперь нет из коробки фичей hibernate, таких как оптимистик лок, кеши, блокировки. Или есть? Не знаю как обстоят дела с транзакцией. Это всё надо теперь самим реализовывать?
Уже потраченные силы и будущие затраты, плюс тот факт, что на проекте теперь не самый популярный рыночный стек. Не слишком ли много лишней работы? Поправьте если я не прав.
Прикольно, не знал что это прям паттерн. У нас в java в Spring Data JPA (самая популярная надстройка над ORM), функциональный интерфейс, который вызывается при формировании предиката, и имеющий and, or - так и называется: Specification.
А что за первоисточник? Черная книга по паттернам корпоративных приложений Фаулера?
Бороться с var бессмысленно. Это факт. Посмотри на количество своих минусов. Единственный способ - тимлид, который запретит использование.
Подсказка в Intelij IDE поможет не сойти с ума. Однако на код ревью, будете и дальше гадать что там за тип.
Если команда адекватная, постарайтесь договориться, о каком-нибудь умном использовании var. Например, только если есть справа new. Или в маленьком приватном методе. Потому что читать простыню кода в перемешку с var и без это гг.
Дженерики, var с ними плохо работает, т.к. кастит к Object. Использование методов возвращающих T, все равно придется писать без var.
var удобен, если тип часто меняется, например был Set, а стал List, не надо трогать тип ссылки.
Если вам не все равно на ваш проект, вы уважаете тех кто читает ваш код, тех кто делает код ревью - не используйте var, или используйте с умом, не создавая дополнительную когнитивную сложность.
При использовании SpringBootTest кешируется спринг контекст. Если сделать класс родитель тестов, прописать в нем эту аннотацию и все end to end тесты наследовать от родителя, то контекст будет построен один раз на все тесты. Но есть нюансы, легко сломать, была статья на этой неделе на Хабре.
Mockito unit test - это прекрасно, но если заинжектить что-то - пройдись по двадцати тестам, допиши что вернет мок, это бесит. Обычно переписываю mockito unit test на component test, если инжектов больше чем 3. Component test это имплементация родительского теста, с контекстом спринга, но без очистки бд.
Mockmvc и mockjpa ни разу не видел на живом проекте. Один раз попробовал, не удобно, component test наше всё.
Отличная статья. Жду полноценную статью от сообщества, как с этим бороться.
Вот несколько моих комментариев, как я работаю с тестами и моментов, которые я пока что не придумал как обойти:
0. По возможности пишем main классы таким образом, чтобы их можно было бы протестировать с помощью unit или @MockitoUnitTest (это те где @InjectMock, я вечно забываю как оно пишется, поэтому даже кастомную аннотацию завел).
1. Если ваш родительский класс с @SpringBootTest делает before и after, в которых полностью очищает таблицы бд, то можно завести наследника, например ComponentTest (как завещал Борисов), который не будет очищать таблицы (т.е. переопределить before и after родителя), но в котором будет полный контекст спринга. И использовать для компонентных тестов. Т.е. для тех тестов, где мокать было бы слишком много, но чистить бд не надо, либо вы сами в конце теста можете почистить только то, что необходимо.
2. @MockBean на все классы, которые ходят по ресту, в родительском классе. Будет удобно мокать результат, контекст спринга закешен.
3. @MockBean на все @Scheduler. Сами же методы шедулеров однострочные, делегируют работу в другой бин, который и тестируем. Контекст спринга закешен. Больше никаких левых select, когда на дебаге пытаешься найти место, откуда вылетает запрос.
4. Фича флаг, через application.property. В тесте рефлекшеном выставляю нужный параметр, в конце теста возвращаю старый. Контекст кешируется. Но такие тесты не переживут рефакторинг. Может есть лучше способ?
Моя боль:
5. @ConditionOnProperty и несколько имплементации бина, подкидываю имплементацию через аннотацию @TestPropertySource над тестом. Контекст не кешируется. Кто как с этим работает?
6. Не придумал пока что, что делать, когда реально нужен @MockBean. Очень жду советов в вашей статье. Пробовал в родительском классе делать @SpyBean, летели исключения, так времени и не было чтобы разобраться до конца. Пока что просто удаляю @MockBean из кода.
Всё-таки самое частое использование List - добавление в конец с последующей итерацией. И в таком кейсе использования ваша структура и LinkedList делают доп нагрузку на хип. К тому же ArrayList можно сразу задать нужного размера.
Использование аннотации @MockBean в тестах, которые будут extends ваш родительский тест с @SpringBootTest, приводит к созданию нового спринг контекста. @SpyBean над бином в родительском тесте и Mockito.when() в дочернем - спринг контекст возьмётся из кеша. Если вы не хотите, чтобы тесты вашего проекта проходили час - надо постараться не плодить контексты спринга.
Я не пробовал, но судя по документации, для "входов" в приложение через @RestController - @RequestScope может прокатить.
Я использовал @RestController в статье для примера визуализации workflow. На практике могут быть и другие "входы" в приложение, например @Scheduler или брокер сообщений, которые не проходят через сервлет, и соответственно @RequestScope не будет работать.
А если бы два метода, помеченные @Scheduled находились бы в разных классах, была бы проблема, что вторая джоба не стартует пока не выполнится первая? Обработчик шедулеров один на приложение или один на класс?
И ещё, если микросервисы + шедлок, то видимо проблема не так актуальна, поток из другой поды сервиса отработает.
Крутяк, спасибо. Единственное, что я бы добавил, по поводу тредлокала. Не забывайте его очищать, или инициализировать каждый раз. Дело в том, что поток может выполнить работу, леч в пул потоков, и пойти делать ту же самую работу, но для других данных, а в тредлокале старые данные. Чуть позже я напишу мини статью об этом, и предложу свои варианты.
Пару лет назад попался обзор на Зевса, там домики строились буквой Г. После чего я прошёл все компании на максимальной сложности. Одна из любимых игр детства.
Вижу граф - ставлю лайк. Хабр, добавите такую фичу? Интересно посмотреть как бы выглядел подграф с "java" или "spring".
А если использовать такой алгоритм?
Пода1 берёт shared lock, делает select .. where status = NEW .. order by .. limit X
Потом для этих записей делает status = IN_PROGRESS, отпускает shared lock и идёт работать с этими записями.
Orange pi какая-нибудь маломощная не была бы надёжнее и удобнее? Вроде не особо дороже стоит, и на неё по ssh можно удаленно зайти.
p.s. я про ESP8266 вообще не в курсе, я джавист, мне надо много памяти кушац
Ага, можно. Проблема в том, что если у тебя сложный запрос, использующий несколько @Entity, то в чьем репозитории его хранить? Вторая проблема, если пихать "одноразовые" методы c @Query в репозиторий, то репозиторий пухнет. Если таких методов немного - @Query будет отличным вариантом.
У вас был вариант писать в нужных местах native query через entity manager. Почему выбрали долгий путь и переписали весь проект на альтернативную технологию вместо этого варианта?
У вас теперь нет из коробки фичей hibernate, таких как оптимистик лок, кеши, блокировки. Или есть? Не знаю как обстоят дела с транзакцией. Это всё надо теперь самим реализовывать?
Уже потраченные силы и будущие затраты, плюс тот факт, что на проекте теперь не самый популярный рыночный стек. Не слишком ли много лишней работы? Поправьте если я не прав.
Прикольно, не знал что это прям паттерн. У нас в java в Spring Data JPA (самая популярная надстройка над ORM), функциональный интерфейс, который вызывается при формировании предиката, и имеющий and, or - так и называется: Specification.
А что за первоисточник? Черная книга по паттернам корпоративных приложений Фаулера?
На ревью приходится разгадывать кроссворды. Не делайте так, пожалуйста.
Бороться с var бессмысленно. Это факт. Посмотри на количество своих минусов. Единственный способ - тимлид, который запретит использование.
Подсказка в Intelij IDE поможет не сойти с ума. Однако на код ревью, будете и дальше гадать что там за тип.
Если команда адекватная, постарайтесь договориться, о каком-нибудь умном использовании var. Например, только если есть справа new. Или в маленьком приватном методе. Потому что читать простыню кода в перемешку с var и без это гг.
Дженерики, var с ними плохо работает, т.к. кастит к Object. Использование методов возвращающих T, все равно придется писать без var.
var удобен, если тип часто меняется, например был Set, а стал List, не надо трогать тип ссылки.
Если вам не все равно на ваш проект, вы уважаете тех кто читает ваш код, тех кто делает код ревью - не используйте var, или используйте с умом, не создавая дополнительную когнитивную сложность.
Логично, что-то не подумал, спасибо, попробую.
При использовании SpringBootTest кешируется спринг контекст. Если сделать класс родитель тестов, прописать в нем эту аннотацию и все end to end тесты наследовать от родителя, то контекст будет построен один раз на все тесты. Но есть нюансы, легко сломать, была статья на этой неделе на Хабре.
https://habr.com/ru/companies/spring_aio/articles/905586/
Mockito unit test - это прекрасно, но если заинжектить что-то - пройдись по двадцати тестам, допиши что вернет мок, это бесит. Обычно переписываю mockito unit test на component test, если инжектов больше чем 3. Component test это имплементация родительского теста, с контекстом спринга, но без очистки бд.
Mockmvc и mockjpa ни разу не видел на живом проекте. Один раз попробовал, не удобно, component test наше всё.
Отличная статья. Жду полноценную статью от сообщества, как с этим бороться.
Вот несколько моих комментариев, как я работаю с тестами и моментов, которые я пока что не придумал как обойти:
0. По возможности пишем main классы таким образом, чтобы их можно было бы протестировать с помощью unit или @MockitoUnitTest (это те где @InjectMock, я вечно забываю как оно пишется, поэтому даже кастомную аннотацию завел).
1. Если ваш родительский класс с @SpringBootTest делает before и after, в которых полностью очищает таблицы бд, то можно завести наследника, например ComponentTest (как завещал Борисов), который не будет очищать таблицы (т.е. переопределить before и after родителя), но в котором будет полный контекст спринга. И использовать для компонентных тестов. Т.е. для тех тестов, где мокать было бы слишком много, но чистить бд не надо, либо вы сами в конце теста можете почистить только то, что необходимо.
2. @MockBean на все классы, которые ходят по ресту, в родительском классе. Будет удобно мокать результат, контекст спринга закешен.
3. @MockBean на все @Scheduler. Сами же методы шедулеров однострочные, делегируют работу в другой бин, который и тестируем. Контекст спринга закешен. Больше никаких левых select, когда на дебаге пытаешься найти место, откуда вылетает запрос.
4. Фича флаг, через application.property. В тесте рефлекшеном выставляю нужный параметр, в конце теста возвращаю старый. Контекст кешируется. Но такие тесты не переживут рефакторинг. Может есть лучше способ?
Моя боль:
5. @ConditionOnProperty и несколько имплементации бина, подкидываю имплементацию через аннотацию @TestPropertySource над тестом. Контекст не кешируется. Кто как с этим работает?
6. Не придумал пока что, что делать, когда реально нужен @MockBean. Очень жду советов в вашей статье. Пробовал в родительском классе делать @SpyBean, летели исключения, так времени и не было чтобы разобраться до конца. Пока что просто удаляю @MockBean из кода.
Интересно, спасибо за статью.
Добавь, пожалуйста, сравнение по памяти.
Всё-таки самое частое использование List - добавление в конец с последующей итерацией. И в таком кейсе использования ваша структура и LinkedList делают доп нагрузку на хип. К тому же ArrayList можно сразу задать нужного размера.
p.s. а ещё есть стримы)
На 20 метровую комнату - два ультразвуковых, с выхлопом 300 в час, с шумом около 30 дБ, без подсветки, с доливом воды сверху.
Практика. Оптимизировал тесты на одном из проектов.
Использование аннотации @MockBean в тестах, которые будут extends ваш родительский тест с @SpringBootTest, приводит к созданию нового спринг контекста. @SpyBean над бином в родительском тесте и Mockito.when() в дочернем - спринг контекст возьмётся из кеша. Если вы не хотите, чтобы тесты вашего проекта проходили час - надо постараться не плодить контексты спринга.
Спасибо за вопрос.
Я не пробовал, но судя по документации, для "входов" в приложение через @RestController - @RequestScope может прокатить.
Я использовал @RestController в статье для примера визуализации workflow.
На практике могут быть и другие "входы" в приложение, например @Scheduler или брокер сообщений, которые не проходят через сервлет, и соответственно @RequestScope не будет работать.
Спасибо, что делишься знаниями.
А если бы два метода, помеченные @Scheduled находились бы в разных классах, была бы проблема, что вторая джоба не стартует пока не выполнится первая? Обработчик шедулеров один на приложение или один на класс?
И ещё, если микросервисы + шедлок, то видимо проблема не так актуальна, поток из другой поды сервиса отработает.
Крутяк, спасибо. Единственное, что я бы добавил, по поводу тредлокала. Не забывайте его очищать, или инициализировать каждый раз. Дело в том, что поток может выполнить работу, леч в пул потоков, и пойти делать ту же самую работу, но для других данных, а в тредлокале старые данные. Чуть позже я напишу мини статью об этом, и предложу свои варианты.
Существует 2 очень похожих паттерна, которые в клиентском коде выглядят как вертикальная запись через точку: builder, fluent interface.
С практической точки зрения я бы классифицировал их следующим образом:
Ленивость - действие выполняется в момент вызова промежуточного метода, или действие выполняется только в момент вызова терминальной операции.
По типу возвращаемого значения. Возвращается тот же самый тип, или другой.
Представленный пример можно назвать ленивым спринговым билдером.