Библиотека Java разработчика
前往频道在 Telegram
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
显示更多📈 Telegram 频道 Библиотека Java разработчика 的分析概览
频道 Библиотека Java разработчика (@bookjava) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 10 123 名订阅者,在 技术与应用 类别中位列第 11 760,并在 俄罗斯 地区排名第 63 248 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 10 123 名订阅者。
根据 25 八月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -74,过去 24 小时变化为 -6,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 7.20%。内容发布后 24 小时内通常能获得 3.71% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 729 次浏览,首日通常累积 376 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 5。
- 主题关注点: 内容集中在 string, интерфейс, строка, boot, api 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.
По всем вопросам @evgenycarter
РКН clck.ru/3KoGeP”
凭借高频更新(最新数据采集于 26 八月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
10 123
订阅者
-624 小时
-137 天
-7430 天
帖子存档
🧠 Spring Boot: правильно логируем
@ExceptionHandler
Сейчас покажу простой, но часто упускаемый момент при обработке ошибок в Spring Boot.
Если у вас есть глобальный @ExceptionHandler, убедитесь, что вы не теряете stacktrace при логировании.
❌ Плохо:
@ExceptionHandler(MyException.class)
public ResponseEntity<String> handle(MyException ex) {
log.error("MyException occurred: {}", ex.getMessage()); // stacktrace теряется!
return ResponseEntity.status(500).body("Error");
}
✅ Хорошо:
@ExceptionHandler(MyException.class)
public ResponseEntity<String> handle(MyException ex) {
log.error("MyException occurred", ex); // stacktrace будет видно в логе
return ResponseEntity.status(500).body("Error");
}
📌 log.error(String, Throwable) — правильный способ логировать исключения. Это позволяет:
* Сохранять stacktrace для дебага;
* Не терять вложенные причины (getCause());
* Работать с лог-агрегаторами (ELK, Grafana, etc).
💡 Если вы используете Slf4j и формат log.error("message: {}", ex.getMessage()), вы теряете почти всю полезную информацию об ошибке.
⚠️ И не забывайте: глобальные хендлеры — это хорошо, но не глушите все ошибки без разбора. Лучше создавать разные @ExceptionHandler под каждую категорию исключений.
📲 Мы в MAX
👉@BookJava🧠 Spring Boot: как НЕ попасть в ловушку с
@Scheduled и многопоточностью
Когда используете @Scheduled для периодических задач в Spring Boot, важно понимать: по умолчанию все задачи выполняются в одном потоке.
@Scheduled(fixedRate = 5000)
public void syncData() {
// долгая операция
}
📌 Если таких задач несколько или они работают долго — остальные ждут. Это создаёт бутылочное горлышко и приводит к неожиданным задержкам.
💡 Решение: настроить кастомный executor:
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5); // количество параллельных задач
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.initialize();
registrar.setTaskScheduler(scheduler);
}
}
Теперь все @Scheduled - методы будут использовать пул потоков, а не один.
⚠️ Не забывайте: если задача критична к ресурсам или зависит от внешних сервисов — добавьте внутреннюю защиту от повторного выполнения, например, с помощью Redis lock или базы.
✅ Подключение пула — must-have для production-проектов, где @Scheduled выполняет реальные задачи, а не просто println.
📲 Мы в MAX
👉@BookJava🚀 Подборка полезных IT каналов в Max
Системное администрирование, DevOps 📌
https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)
Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак
Английский с нуля 🇬🇧
https://max.ru/UchuEnglish
1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8
Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek
Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика
Программирование React📌
https://max.ru/react_lib React
Программирование Rust📌
https://max.ru/channel_rust_lib
Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика
Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek
GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных
Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков
Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов
Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼💻👩💻
Шутки программистов 📌
https://max.ru/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free
Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров
Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике
Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait
Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных
Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
💡 Не делай этого в
@PostConstruct — особенно в проде
Сейчас расскажу, почему инициализировать важную бизнес-логику в @PostConstruct — плохая идея.
Типичный пример:
@Component
public class CacheLoader {
private final SomeService service;
public CacheLoader(SomeService service) {
this.service = service;
}
@PostConstruct
public void init() {
service.loadDataIntoCache(); // ⚠️ обращение к БД
}
}
🧨 Проблема: @PostConstruct вызывается до того, как приложение полностью поднялось.
Если внутри будет ошибка (например, БД недоступна) — приложение может упасть, или что хуже — запуститься в полурабочем состоянии.
📌 Кроме того:
* ❌ Нет контроля над порядком выполнения таких методов;
* ❌ Нельзя легко переиспользовать эту логику (например, вручную перезагрузить кеш);
* ❌ В тестах или dev-среде — такие вызовы часто мешают.
✅ Современный подход — использовать ApplicationListener:
@Component
public class CacheLoader implements ApplicationListener<ApplicationReadyEvent> {
private final SomeService service;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
service.loadDataIntoCache(); // 👍 вызывается только после старта
}
}
📌 Альтернатива — аннотация @EventListener:
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
// безопасно загружаем данные
}
📦 В Spring Boot это нативный и рекомендованный способ выполнения кода после старта.
🧠 Резюме:
🔹 @PostConstruct — только для простой инициализации бинов.
🔹 Бизнес-логику и I/O — в @EventListener(ApplicationReadyEvent.class).
📲 Мы в MAX
👉@BookJava🧠 JVM-разработчики, стягиваемся в Москву 29 августа
В последнюю субботу лета бэкенд- и JVM-комьюнити соберется на JVM Day, который устраивает Т-Банк. Ждут всех разработчиков, архитекторов и тимлидов, которые работают с Java, Kotlin и Scala.
В программе:
– Андрей Кулешов расскажет о том, что изменилось в мире JVM за год.
– Сергей Петрелевич разберет Mechanical Sympathy.
– Александр Ланцов покажет, какие модели конкурентности актуальны после появления Loom.
– Антон Курако объяснит, почему красивые показатели бенчмарков могут вводить в заблуждение.
А после докладов во дворе штаб-квартиры Т-Банка пройдут:
– Игры на Sega и ИТ-Квиз.
– PowerPoint Party и открытый микрофон с историями ошибок.
– Диджей-сет на виниле под открытым небом.
Места ограничены, регистрироваться тут. Пишите в комментариях, если идете на конфу.
📲 Мы в MAX
👉@BookJava
🧠
@Value в Spring — это ловушка, если вы используете списки или map'ы
Многие знают, что можно заинжектить список строк из application.yml вот так:
app:
langs:
- en
- fr
- de
@Value("${app.langs}")
private List<String> langs;
Но знаете, что вы получите?
⚠️ ОШИБКУ. @Value не умеет парсить YAML-массивы. Он ожидает строку, и даже с CSV-строкой (en,fr,de) — всё не так очевидно: Spring не применяет ConversionService для списков.
📌 Решение — использовать @ConfigurationProperties:
app:
langs:
- en
- fr
- de
@ConfigurationProperties(prefix = "app")
@Component
public class AppProps {
private List<String> langs;
// геттеры/сеттеры
}
💡 Профит:
- работает с List, Map, вложенными объектами;
- валидация через @Validated и @NotEmpty;
- легко покрыть тестами;
- меньше магии.
⚠️ @Value хорош для простых скаляров. Всё остальное — через @ConfigurationProperties.
📲 Мы в MAX
👉@BookJava👩💻 Работа с Kafka через библиотеку Kafka Clients
Приглашаем на открытый урок.
🗓 26 августа в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java разработчик. Продвинутый уровень».
Kafka Clients это нижний уровень для работы с Kafka из Java. Все более высокоуровневые библиотеки основаны на нем.
Данный открытый урок проходит в рамках курса Java разработчик. Продвинутый уровень.
О чем поговорим:
✔️ Чтение и отправка сообщений в Kafka
✔️ Управление топиками
✔️ Разные форматы: текст, JSON, Avro
✔️ Использование транзакций
🔗 Ссылка на регистрацию: https://vk.cc/d0zMZ5
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Нюанс с
Optional.map() и методами, возвращающими Optional
Многие Java-разработчики на автомате пишут что-то вроде:
Optional<User> user = findUserById(id); // возвращает Optional<User>
Optional<String> email = user.map(User::getEmail); // getEmail тоже возвращает Optional<String>
⚠️ Проблема: map() в Optional не "разворачивает" вложенные Optional. В этом примере email будет типа Optional<Optional<String>>, что почти всегда нежелательно.
📌 Правильный способ — использовать flatMap():
Optional<String> email = user.flatMap(User::getEmail);
💡 flatMap() позволяет избежать "двойной обёртки", если метод внутри map() уже возвращает Optional.
🔁 Аналогичная ситуация с Stream.map() — если внутри map() вызывается метод, возвращающий Stream, то получится Stream<Stream<T>>, и опять же нужно использовать flatMap().
🧪 Мини-памятка:
* map() — когда метод возвращает обычный тип (T → R);
* flatMap() — когда метод возвращает Optional или Stream (T → Optional<R> или T → Stream<R>).
📲 Мы в MAX
👉@BookJavaПочему ты получаешь отказ после "норм собеса"?
Дело не в знаниях - ты просто не знаешь, что реально проверяют на собеседованиях
Отсюда:
— непонимание, какие навыки надо качать, чтобы выйти на следующий грейд
— ощущение стагнации и отсутствие роста в скиллах
— долгое время на одной и той же зп
Решение — знать, что от тебя ждёт интервьюер, и умение правильно донести свою мысль
Для этого ребята из ШОРТКАТ (менторской программы для разработчиков) собрали в один файл ОТВЕТЫ НА 50 СЛОЖНЫХ ВОПРОСОВ с Java-собеседований 🔥
Они проанализировали более 100 мок-интервью и собрали внутри реальные вопросы + ответы сильного кандидата
Забрать можно бесплатно (!) в боте — @shortcut_sh_bot
Переходите по ссылке выше и забирайте файл, чтобы быстрее выйти на новый уровень ☝️
Реклама.
О рекламодателе.
Java. Сортировки
Java. Сортировка пузырьком.
Java. О сортировке выбором.
Java. Быстрая сортировка. Объяснение на пальцах)
Java. Оценка сложности алгоритмов сортировки.
Java. Сортировка слиянием.
Java. Сортировка подсчетом.
Java. Сортировка вставками.
Java. Сортировка расческой. От пузырька до расчески.
📲 Мы в MAX
👉@BookJava
🧠 Как в Java сломать equals() и потерять данные в HashMap
Сегодня покажу, как неочевидный баг в
equals()/hashCode() может привести к потере данных при работе с HashMap.
Допустим, у вас есть Entity:
public class User {
private String id;
private String name;
// equals/hashCode только по id
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Вроде норм. Но теперь добавим такого юзера в HashMap, а потом... изменим его id:
User user = new User();
user.setId("1");
user.setName("Alice");
Map<User, String> map = new HashMap<>();
map.put(user, "value");
user.setId("2"); // ⚠️ ключ стал "невидимым"
System.out.println(map.get(user)); // null 😱
📌 Почему так происходит?
HashMap ищет ключ по hashCode() → ищет бакет → сравнивает через equals(). А hashCode() уже другой, и объект "теряется".
💡 Совет: если вы используете объект как ключ в мапе или добавляете его в Set, не изменяйте его поля, участвующие в equals()/hashCode()!
📌 А как правильно?
- Делайте такие поля final;
- Или используйте неизменяемые типы (record);
- Или не используйте такие объекты как ключи вовсе.
Вот безопасный вариант с record:
public record User(String id, String name) {}
📲 Мы в MAX
👉@BookJava💡Сегодня покажу крутую фишку для оптимизации чтения больших коллекций из базы через JPA.
📌 Проблема:
Когда загружаем большую коллекцию через
@OneToMany, Hibernate часто делает это лениво (LAZY), но при первом доступе — забирает всю коллекцию целиком.
Это может привести к OutOfMemoryError или резкому проседанию производительности.
📌 Решение: использовать пагинацию (batch-size) или запрос коллекции порциями.
Как настроить batch-size на уровне сущности:
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@BatchSize(size = 50)
private List<Order> orders;
}
🧠 Теперь Hibernate будет загружать за раз по 50 элементов, а не всю коллекцию сразу!
📌 Или можно настроить глобально через application.properties:
spring.jpa.properties.hibernate.default_batch_fetch_size=50
⚠️ Важно:
- @BatchSize работает только для LAZY-связей.
- Это не пагинация в SQL, а оптимизация внутренних запросов Hibernate.
- Если коллекция огромная (100k+ записей) — лучше делать явные paged запросы в репозитории.
💡Помните: без настройки batch-size Hibernate может сломать приложение под нагрузкой. Оптимизируйте загрузку коллекций заранее!
📲 Мы в MAX
👉@BookJavaКак работают instance initializer blocks.
Пример с родительским и дочерним классами:
class Parent {
int a = 5;
{
System.out.println("Parent instance initializer");
a = 10;
}
Parent() {
System.out.println("Parent constructor, a = " + a);
}
}
class Child extends Parent {
int b = 15;
{
System.out.println("Child instance initializer");
b = 25;
}
Child() {
System.out.println("Child constructor, b = " + b);
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
}
}
Вывод программы:
Parent instance initializer Parent constructor, a = 10 Child instance initializer Child constructor, b = 25Пошаговое выполнение: 1. Сначала загружается родительский класс
Parent.
2. Выполняется:
- Инициализация полей родителя (a = 5),
- Потом instance initializer блока родителя (a = 10),
- Потом конструктор родителя (Parent()).
3. Далее переходим к дочернему классу Child:
- Инициализация полей дочернего класса (b = 15),
- Потом instance initializer блока дочернего класса (b = 25),
- Потом конструктор дочернего класса (Child()).
Важный порядок действий:
1. Инициализация родителя → 2. Конструктор родителя → 3. Инициализация потомка → 4. Конструктор потомка.
Блоки инициализации всегда выполняются до тела конструктора, но после вызова super().
📲 Мы в MAX
👉@BookJava🚀 Подборка полезных IT каналов в Max
Системное администрирование, DevOps 📌
https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)
Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак
Английский с нуля 🇬🇧
https://max.ru/UchuEnglish
1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8
Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek
Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика
Программирование React📌
https://max.ru/react_lib React
Программирование Rust📌
https://max.ru/channel_rust_lib
Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика
Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek
GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных
Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков
Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов
Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼💻👩💻
Шутки программистов 📌
https://max.ru/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free
Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров
Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике
Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait
Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных
Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
В Java instance initializer blocks (блоки инициализации экземпляра) выполняются в следующем порядке:
- Они выполняются каждый раз, когда создается новый объект класса.
- Выполнение происходит после вызова конструктора родительского класса (
super()), но до тела конструктора текущего класса.
Порядок инициализации:
1. Сначала инициализируются поля в порядке их объявления.
2. Затем выполняются instance initializer blocks, в том порядке, в котором они написаны в коде.
3. После этого выполняется тело конструктора.
Пример:
class Example {
int x = 10;
{
System.out.println("Instance initializer block");
x = 20;
}
Example() {
System.out.println("Constructor");
System.out.println("x = " + x);
}
public static void main(String[] args) {
Example ex = new Example();
}
}
Вывод:
Instance initializer block Constructor x = 20Ключевые моменты: - Статические блоки (
static {}) — другое дело: они выполняются один раз при загрузке класса.
- Instance initializer blocks полезны для общей инициализации, которую нужно выполнять вне зависимости от того, какой конструктор вызывается.
📲 Мы в MAX
👉@BookJava🔴 Завтра тестовое собеседование с Java-разработчиком
5 августа(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.
Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама.
О рекламодателе.
🧠 Сегодня покажу вам полезный трюк для Spring Boot: как элегантно валидировать параметры контроллера без лишнего кода.
Часто вижу, как в контроллерах вручную проверяют
@RequestParam на пустоту или формат. Это шумит код и приводит к ошибкам.
Вместо этого используйте аннотации валидации прямо на параметрах:
@RestController
@RequestMapping("/api")
@Validated // Обязательно!
public class UserController {
@GetMapping("/users")
public List<User> getUsers(
@RequestParam @NotBlank String name,
@RequestParam @Min(18) int age
) {
// Если валидация не пройдена — автоматически вернётся 400 Bad Request
return userService.findUsers(name, age);
}
}
📌 Ключевые моменты:
- Обязательно ставим @Validated над классом контроллера.
- На параметры добавляем любые стандартные аннотации из jakarta.validation.constraints.
- Ошибки валидации Spring обработает автоматически через MethodArgumentNotValidException.
💡 Можно кастомизировать ответ на ошибку, добавив глобальный @ExceptionHandler.
⚠️ Без @Validated аннотации на контроллере валидация параметров работать не будет!
📲 Мы в MAX
👉@BookJava🧠 JPA: подводный камень с
@ElementCollection и fetch = FetchType.EAGER
Сегодня покажу, почему @ElementCollection(fetch = FetchType.EAGER) — скрытая угроза производительности и неожиданного поведения.
📌 Суть проблемы
При использовании @ElementCollection с EAGER Hibernate делает отдельный SELECT на каждую коллекцию, даже при JOIN FETCH на родительскую сущность.
@Entity
class User {
@Id Long id;
@ElementCollection(fetch = FetchType.EAGER)
List<String> tags;
}
Запрос findAll() приведёт к N+1 проблеме:
1 запрос на User, потом по одному на каждый tags.
💡 Почему это больно
Даже если вы используете JOIN FETCH на User, Hibernate не может сделать JOIN на @ElementCollection. Это ограничение — Hibernate всегда грузит коллекцию отдельным запросом.
⚠️ Особенно опасно при pagination
Если вы делаете Page<User> — Hibernate сначала грузит User'ов, а затем делает N запросов на коллекции. В проде это быстро становится проблемой.
✅ Что делать
1. Делайте fetch = FetchType.LAZY (по умолчанию так и есть).
2. Если нужно подгрузить коллекцию — используйте @BatchSize:
@ElementCollection
@BatchSize(size = 20)
List<String> tags;
Или вручную:
SELECT u FROM User u LEFT JOIN FETCH u.tags WHERE u.id IN :ids
👉 Следи за @ElementCollection — он не так прост, как кажется.
📲 Мы в MAX
👉@BookJava🧠 Как избежать N+1 при использовании
@OneToMany в JPA
Одна из самых коварных ловушек JPA - это N+1 проблема. Особенно часто она проявляется при @OneToMany, например:
@Entity
class Author {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Book> books;
}
Вы загружаете список авторов, а потом проходите по каждому и вызываете getBooks() — и BAM 💥: 1 запрос на авторов и N запросов на книги.
📌 Решение - @EntityGraph или JOIN FETCH
Оба варианта решают проблему, но @EntityGraph — декларативный и более гибкий способ:
@EntityGraph(attributePaths = "books")
List<Author> findAll(); // Spring Data JPA
⚡️ Или через JPQL:
@Query("SELECT a FROM Author a JOIN FETCH a.books")
List<Author> findAllWithBooks();
💡 Совет: всегда думайте о графе объектов. Если вам нужно сразу подтянуть связанные сущности — делайте это явно. Не надейтесь на LAZY по умолчанию.
⚠️ Осторожно с пагинацией и JOIN FETCH — могут появиться дубликаты или проблемы с LIMIT. В таких случаях лучше использовать @BatchSize или подзапросы.
📲 Мы в MAX
👉@BookJavaЧем отличаются checked и unchecked исключения в java?
В Java исключения делятся на два основных типа: checked и unchecked. Разница между ними ключевая и касается как обработки, так и структуры кода.
✅ Checked Exceptions (Проверяемые исключения)
Примеры: IOException, SQLException, FileNotFoundException
Отличия:
1. Проверяются компилятором — вы обязаны либо обработать их с помощью try-catch, либо явно пробросить (throws) в сигнатуре метода.
2. Производные от Exception, но не от RuntimeException.
3. Используются для ситуаций, которые можно разумно ожидать и обработать (например, отсутствие файла, проблемы с сетью).
Пример:
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // может выбросить IOException
}
❌ Unchecked Exceptions (Непроверяемые исключения)
Примеры: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException
Отличия:
1. Не проверяются компилятором — вы не обязаны их обрабатывать или декларировать.
2. Производные от RuntimeException.
3. Возникают в результате ошибок в логике программы или непредвиденных ситуаций.
Пример:
public void printLength(String str) {
System.out.println(str.length()); // может выбросить NullPointerException
}
Если кратко:
Checked — ошибки среды, требующие явной обработки.
Unchecked — ошибки в логике, ответственность программиста.
📲 Мы в MAX
👉@BookJava