Spring АйО
8.44K subscribers
304 photos
215 videos
404 links
Русскоязычное сообщество Spring-разработчиков.

Habr: bit.ly/433IK46
YouTube: bit.ly/4h3Ci0x
VK: bit.ly/4hF0OG8
Rutube: bit.ly/4b4UeX6
Яндекс Музыка: bit.ly/3EIizWy

Канал для общения: @spring_aio_chat
Download Telegram
🐢 Неожиданное падение производительности при переходе на Spring Boot 3.3.0 👩‍💻

Hibernate подложил небольшую свинью, конвертируя JPQL в SQL. Внезапно, запрос поиска записей по списку id с передачей пустого списка деградирует в table full scan.

Однако, Hibernate пофиксил баг в течении 10 часов после поста в запрещенную в РФ соцсеть. Осталось дождаться новой версии Spring Boot, которая включит себе этот фикс.

🔗Подробнее читайте на Хабре

#SpringBoot #Hibernate #BreakingNews
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯14😱5👍3🔥2😁2
🎙 Spring АйО Чат!

Друзья, напоминаем, что у нас есть чат (@spring_aio_chat), в котором вы можете:

💬 Делиться фидбеком о публикуемых материалах;
Задавать вопросы, связанные со Spring и сопутствующими технологиями;
😼 Отвечать на вопросы, если Вы знаете на них ответ.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍4🌚2
Неблокирующие сервисы вчера: WebFlux, Coroutines и Loom.
"Неблокирующие сервисы" сегодня: DockerHub, IntelliJ IDEA Ultimate, Slack и другие.
#программисты_шутят
😁263🔥2💩2👏1
Dynamic Projection в Spring Data JPA

Используя Spring Data, довольно часто могут возникать накладные расходы из-за получения из базы слишком большого количества полей. Чтобы решить эту проблему, мы можем объявлять методы с нативными запросами, DTO или проекциями для случаев, когда необходимо ограничить количество выбираемых полей. Но такой подход приводит к появлению множества очень похожих методов в репозитории. Например:


public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByName(String name); // выбираем из базы сущность со всеми её полями

List<ProductProjection> findPrjsByName(String name); //выбираем из базы ограниченный набор полей, указанный в некоторой проекции

List<ProductDto> findDtosByName(String name); //выбираем из базы ограниченный набор полей, указанный в некоторой DTO
}


Чтобы решить эту проблему можно прибегнуть к использованию Dynamic Projection:


public interface ProductRepository extends JpaRepository<Product, Long> {
<T> List<T> findByName(String name, Class<T> type);
}


Подробнее о сценариях использования и компромиссах, на которые придется пойти используя Dynamic Projection, читайте в следующей статье: https://maciejwalkowiak.com/blog/spring-data-jpa-dynamic-projections/
🔥15👍11👌3
Мы рады сообщить, что наше сообщество начинает обрастать экспертами, и мы будем знакомить вас с их мнением. Встречайте, Михаил Поливаха, коммитер Spring Data JDBC (Relational), не удержался от комментария по вопросу поддержки проекций в разных модулях Spring Data. Ниже прямая речь Михаила.

Надо сказать, что projection-ы, как функциональность, должна быть доступна во всех модулях spring-data. Вопрос в том, что еще не все модули ее поддержку реализовали до конца.
Наивный читатель мог бы предположить, что поскольку в spring-data-jdbc есть поддержка как interface based projection-ов, так и dynamic projection-ов, то вот это заработает:


public interface MyEntityRepository extends CrudRepository<MyEntity, Long> {

<T> Optional<T> findProjectionByStatus(String status, Class<T> clazz);
}

@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Accessors(chain = true)
@Table
public class MyEntity {

@Id
@EqualsAndHashCode.Include
private Long id;

private String status;

@CreatedDate
private Instant createdAt;
}

interface MyEntityInterfaceProjection {
String status();

Long id();
}

@Test
void whenDynamicProjectsAreInUseViaInterfaces_thenBlast() {
saveInitialEntity();
Optional<MyEntityInterfaceProjection> active = myEntityRepository.findProjectionByStatus("active", MyEntityInterfaceProjection.class);

Assertions.assertThat(active)
.isPresent()
.hasValueSatisfying(myEntityProjection -> {
Assertions.assertThat(myEntityProjection.id() != null && myEntityProjection.status().equalsIgnoreCase("active")).isTrue();
});
}

@NotNull
private MyEntity saveInitialEntity() {
MyEntity myEntity = new MyEntity();
myEntity.setStatus("active");
return myEntityRepository.save(myEntity);
}


Но данный код сгенерит ошибку глубоко в недрах JdbcQueryCreator-a:


java.lang.IllegalStateException: SELECT does not declare a select list
at org.springframework.data.relational.core.sql.SelectValidator.doValidate(SelectValidator.java:57)
at org.springframework.data.relational.core.sql.SelectValidator.validate(SelectValidator.java:49)
at org.springframework.data.relational.core.sql.DefaultSelectBuilder.build(DefaultSelectBuilder.java:207)
at org.springframework.data.jdbc.repository.query.JdbcQueryCreator.complete(JdbcQueryCreator.java:173)
at org.springframework.data.jdbc.repository.query.JdbcQueryCreator.complete(JdbcQueryCreator.java:66)


Связано это с тем, что на данный момент spring-data-jdbc просто не умеет переваривать динамические проджекшены для инфтерфейсов 🙂
Про это мы знаем, бага на это дело пока нет, если он кому-либо важен - можем завести. Хочу также напомнить, что если есть какие-либо инициативы для нововведений в spring-data-jdbc - можно оставлять в комментариях или писать мне в ЛС.
🔥19👍8👏3
⚠️ Уязвимость в плагине JetBrains GitHub

Если вы используете плагин GitHub в IntelliJ IDEA или других продуктах JetBrains, советуем обновиться до последней версии и отозвать токены созданные IDEA в своем GitHub аккаунте.

Подробности в статье.

🔗 https://habr.com/ru/companies/spring_aio/articles/820819/
😱12🤔54😐21
🔧 Spring Data findAll антипаттерн

Spring Data Repository отличная концепция, позволяющая нам абстрагироваться от CRUD операций над domain entity. Достаточно объявить пустой интерфейс и унаследовать его, например, от ListCrudRepository или JpaRepository, если мы работем с JPA.


public interface OwnerRepository extends JpaRepository<Owner, Long> {
}


После этого нам сразу будут доступны основные методы работы с entity - save, update, findById, findAllById, findAll, и т.д. Но некоторые из этих методов могут привести к серъезным проблемам с производительностью и памятью, например метод findAll(). Давайте представим, что нам нужно найти всех Owner у которых есть животные c определенным именем и отсортировать по имени владельца. Поскольку мы находимся всего в одном шаге от вызова findAll() метода для «решения» этой проблемы, не пройдет много времени, пока кто-нибудь не предложит следующее решение:



public List<Owner> findOwnerByPetName(Collection<String> petNames) {
return ownerRepository.findAll()
.stream()
.filter(owner -> owner.getPets()
.stream()
.map(Pet::getName)
.anyMatch(petNames::contains)
)
.sorted(Comparator.comparing(Owner::getFirstName))
.toList();
}


Проблема здесь в том, что мы загрузили всю таблицу Post в память и потом начали фильтровать и сортировать данные, вместо того чтобы сделать это с помощью JPQL или SQL запроса за одно обращение к БД и загрузить в память только те данные, которые нам нужны в дальнейшем. Давайте посмотрим как мог бы выглядеть такой метод репозитория:


//derived method
List<Owner> findByPets_NameInOrderByFirstNameAsc(Collection<String> petNames);

//JPA query method
@Query("select o from Owner o left join o.pets pets where pets.name in ?1 order by o.firstName")
List<Owner> findOwnerByPetName(Collection<String> petNames);



Если у нас в репозитории есть метод findAll(), мы должны понимать что рано или поздно, по мере роста команды, им может кто-то воспользоваться. Возможно следует определять базовый интерфейс репозитория вашем проекте самостоятельно и наследовать его от org.springframework.data.repository.Repository и подконтрольно наполнять его методами.

#SpringData #SpringTips
👍235👀3👎2😴2
🤣20😁7👍2🤔1
🕓 Hibernate и спецификация JPA: приключение на 20 минут 24 часа

На прошлой неделе у нас был пост про внезапное падение производительности в Hibernate 6.5 на достаточно простом запросе. А сегодня вы можете прочитать историю этой проблемы от лица Гевина Кинга, создателя Hibernate.

Гевин Кинг рассказал, как возник этот баг, и как он повлиял на спецификацию JPA 3.2.

📚 Подробней читайте на Хабре
👍13🔥4🤯3👏1
По умолчанию в IntelliJ IDEA работают следующие комбинации клавиш:

1) ⌥ + ← (CTRL + ←) – передвинуть каретку к началу слова
2) ⌥ + → (CTRL + →) – передвинуть каретку к концу слова
3) ⇧ + ⌥ + ← (⇧ + CTRL + ←) – выделить слово до его начала
4) ⇧ + ⌥ + → (⇧ + CTRL + →) – выделить слово до его конца
5) ⌥ + ⌫ (CTRL + ⌫) – удалить слово с текущей позиции каретки и до его начала

Но мало кто знает, что эти шорткаты можно сделать еще удобнее!

Настройте их же для режима CamelHumps и IntelliJ IDEA начнёт понимать слова написанные в camelCase!

#IntelliJIDEA #Tooling #HotKeys
This media is not supported in your browser
VIEW IN TELEGRAM
👍187🔥3👏2🥱2🤨1
Разработчики Каркаса Весеннего Ботинка утверждают, что эти фразы имеют смысл:

- "У меня не применяется совет контроллера"
- "Тебе нужно выстрелить событием"
- "Я сделал этот боб главным, чтобы мои анализы не провалились"

My controller advice did not apply
You need to fire an event
I made this bean primary in order to not to fail my tests


Они держат нас за дураков?
😁36👍1👎1🔥1
Внедряем Jakarta Data в Spring приложение

Как Java разработчик, вы, скорее всего, хорошо знаете о шаблоне Repository и связанных с ним возможностях, которые уже предоставлены в популярных фреймворках, таких как Spring Data, Quarkus ORM Panache, Micronaut Data и других. Но у каждого фреймворка есть свои преимущества и ограничения.

Jakarta Data — это новая спецификация Jakarta EE, которая пытается создать универсальные интерфейсы для доступа к реляционным и нереляционным базам данных.


Так выглядит запрос с использованием Spring Data:

public interface PostRepository<Post, UUID> extends JpaRepository {
List<Post> findByStatus(Status status);
}


А вот так с Jakarta Data:

@jakarta.data.repository.Repository
public interface PostRepository extends CrudRepository<Post, UUID> {
@Find
@OrderBy("createdAt")
List<Post> byStatus(Status status);
}


P.S. Выглядит классно, но всё еще не понятно: а зачем это надо, если уже есть Spring Data?). Будем рады обсуждению в комментариях 💬

🔗 https://itnext.io/integrating-jakarta-data-with-spring-0beb5c215f5f (нужен VPN)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥3😁31
👩‍💻 Spring Boot 3.2: замените свой RestTemplate на RestClient

В мире Spring Boot отправка HTTP запросов к внешним сервисам является весьма распространенной задачей. Традиционно при достижении этой цели разработчики полагались на RestTemplate. Однако, по мере развития Spring Framework, на свет появился новый и более мощный способ обработки HTTP запросов: так называемый WebClient. Spring Boot 3.2 представил нам надстройку над WebClient, которая получила название RestClient.

RestClient предлагает нам более современные и интуитивно понятные способы взаимодействия с RESTful сервисами.

📚 Подробности в статье
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27🔥7👏41
🌿Spring Framework – история проекта и его названия

Часто мы принимаем многие вещи на веру, не углубляясь в причину их происхождения.

Что же заставило Рода Джонсона (создателя Spring Framework), дать название «Spring» своему фреймворку? Если вы никогда об этом не думали, то эта статья из 2006 года именно для вас!

TL;DR: Название «Spring» было предложено в конце 2002 года и символизировало начало нового периода после «зимы» с традиционным J2EE.

🔗 https://spring.io/blog/2006/11/09/spring-framework-the-origins-of-a-project-and-a-name
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍64
🧭 Spring Boot Developer Roadmap

Roadmap для изучения Spring Boot: https://roadmap.sh/spring-boot. Сохраняйте, чтобы не потерять!

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

Будем рады обсуждению в комментариях!

😌 Подписывайтесь на канал: @spring_aio
💬 Вступайте в чат: @spring_aio_chat
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥63👏2👎1
Новый компилятор K2 в Kotlin. Часть 1

В новой статье эксперт сообщества Spring АйОМихаил Поливаха рассмотрит новый компилятор К2 для Kotlin. Сначала он расскажет о том, какие проблемы K2 призван решить, а затем о других минорных улучшениях, которые были сделаны. Гайд по обновлению на новую версию будет опубликован в следующей части этой статьи.

📚 Подробней читайте на Хабре
👍15🔥532
🗓 Еженедельный дайджест №1

Для тех, кто был слишком занят на неделе или просто пропустил некоторые посты, публикуем дайджест!

- Внедряем Jakarta Data в Spring приложение. Обсуждение в комментариях получилось горячим!

- Spring Boot 3.2: замените RestTemplate на RestClient. Кратко объяснили, что такое RestClient и зачем он нужен.

- Spring Framework – история проекта и его названия. Нашли ответ на важный вопрос: "Почему же Spring?" в статье от 2006 года.

- Spring Boot Developer Roadmap. Обсудили, какие технологии должен знать современный Spring Boot разработчик.

- Новый компилятор K2 в Kotlin. Часть 1. Разобрали, чем может похвастаться обновленный компилятор в Kotlin.


И главное, нас уже больше 1000! Спасибо, что вы с нами 😎

😌 Подписывайтесь на канал: @spring_aio
💬 Вступайте в чат: @spring_aio_chat
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥33👍135👏1
🧪Тестирование HTTP-эндпоинтов: MockMvc vs. WebTestClient vs. TestRestTemplate

Spring предлагает различные инструменты для тестирования контроллеров: MockMvc, WebTestClient и TestRestTemplate. И пусть все они служат одной цели (вызову HTTP-эндпоинтов и проверке ответа), между ними есть некоторые различия.

📎 В статье найдете обзор того, где и как использовать эти три класса, а также в чем их отличия (en): https://rieckpil.de/spring-boot-testing-mockmvc-vs-webtestclient-vs-testresttemplate/

TL;DR: основные различия приведены на картинке.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍144🔥3
⚡️ Эффективность Spring-приложений в рантайме. Текущее состояние дел и планы на будущее

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

Себастьян Делюз, контрибьютор Spring Framework, делится обзором усилий команды Spring по оптимизации эффективности приложений во время выполнения. Он рассматривает Virtual Threads, GraalVM Native Image, Project CRaC, Project Leyden и всё это в контексте Spring!

📚 Читать на Хабр: https://habr.com/ru/companies/spring_aio/articles/824328/

P.S. Будем благодарны за лайки. Конечно же, если перевод статьи вам понравится 💚
Please open Telegram to view this post
VIEW IN TELEGRAM
👍30🔥632👏1
Spring Tip: Customizer интерфейсы для кастомизации бинов

Одной из главных причин популярности Spring Boot является его способность автоматически конфигурировать (auto-configuration) множество компонентов, существенно упрощая жизнь разработчикам. Однако иногда возникает необходимость слегка подправить настройки этих компонентов без отказа от всех преимуществ автоматической конфигурации.

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


@Bean
CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return cacheManager -> cacheManager.setAllowNullValues(false);
}


Аналогичным образом можно настроить и любые другие компоненты:


//Кастомизация свойств Hibernate
@Bean
HibernatePropertiesCustomizer hibernatePropertiesCustomizer() {
return properties -> properties.put("hibernate.integrator_provider",
(IntegratorProvider) () -> List.of(new BeanValidationIntegrator()));
}

//Кастомизация Jackson2ObjectMapperBuilderCustomizer
@Bean
Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
builder.featuresToEnable(
SerializationFeature.WRITE_ENUMS_USING_TO_STRING,
DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
};
}


Но бывает и такое, что подходящего Customizer интерфейса просто нет. В таком случае, можно использовать BeanPostProcessor для кастомизации уже инициализированных бинов:


//Кастомизация springLiquibase бина
@Bean
BeanPostProcessor liquibaseBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SpringLiquibase springLiquibase) {
springLiquibase.setContexts("my-context");
}
return bean;
}
};
}


#SpringBoot #SpringTips
Please open Telegram to view this post
VIEW IN TELEGRAM
👍38🔥99