📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP
👩💻 Хотите выйти за пределы стандартных подходов в Java-разработке? Разобраться в JVM, многопоточности и современных фреймворках?
🔥 Актуальное обучение, курс «Java Developer. Professional» — это 96 часов практики, детальный разбор технологий, код-ревью от опытных экспертов и работа с Spring WebFlux, Kafka, Kubernetes.
После обучения вы сможете разрабатывать сложные Java-приложения уровня Middle+, понимать работу JVM изнутри и писать чистый, оптимизированный код.
🎁 Дарим промокод, который дает скидку на обучение - JAVA_06
➡️ Пройдите вступительное тестирование и получите скидку: https://vk.cc/cMonpN
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Сегодня разберём Saga — паттерн, который часто всплывает на собеседованиях уровня сеньор, особенно если речь о микросервисах.
📌 Зачем нужна Saga?
Когда нужно выполнить бизнес-операцию, затрагивающую несколько микросервисов, важно обеспечить целостность данных. Классические транзакции (@Transactional
) тут не подходят — нет распределённого ACID. Saga решает эту проблему через последовательность локальных транзакций с возможностью отката (compensation).
💡 Виды Saga
1. Choreography
Нет единого оркестратора. Каждый сервис реагирует на события предыдущих через очередь (Kafka, RabbitMQ).
* Простой flow
* Меньше точек отказа
2. Orchestration
Есть выделенный "оркестратор", который координирует выполнение саги, рассылая команды сервисам.
* Более явный контроль
* Лучше трассировка
⚖️ Плюсы
* Обеспечивает согласованность между сервисами
* Высокая отказоустойчивость: каждый шаг можно компенсировать
* Гибкость — легко расширять и модифицировать бизнес-логику
⚠️ Минусы
* Сложнее реализовать, чем простые транзакции
* Не моментальная консистентность — возможны временные аномалии
* Нужно проектировать compensating transactions для каждого шага
* Тяжело тестировать крайние случаи и "сорванные" шаги
🔁 Альтернативы
* Distributed Transactions (XA, 2PC) — редко используются из-за сложности и слабой поддержки в современных cloud-системах.
* Event Sourcing — другой паттерн работы с изменениями, но сложнее для чтения.
* Idempotency + Retry — иногда достаточно, если бизнес-процесс допускает.
💡 Совет: для большинства бизнес-операций с межсервисным взаимодействием, где требуется отмена — Saga остаётся самым практичным выбором. В Spring есть библиотека Axon Framework, также стоит посмотреть на Camunda.
👉@BookJava
🧠 Как быстро написать компаратор в Java и не забыть про null
Часто нужно сортировать коллекции по какому-то полю. Вместо старых анонимных классов используем лямбды и статические методы из Comparator
:
List<User> users = ...;
users.sort(Comparator
.comparing(User::getName, Comparator.nullsLast(String::compareToIgnoreCase))
.thenComparingInt(User::getAge)
);
comparing
— сравнивает по полю (например, name).nullsLast
или nullsFirst
— удобно обрабатывать возможные null.thenComparingInt
— дополнительная сортировка (например, по возрасту).NullPointerException
во время сортировки может неожиданно прилететь в проде.📊 Приглашаем на открытый урок: Создание RLE архиватора на Java
Хотите узнать, как работают алгоритмы сжатия данных?
В нашем вебинаре мы шаг за шагом создадим архиватор на Java, используя алгоритм RLE. Вы разработаете интерфейс программы и поймете, как реализовать самые эффективные методы сжатия.
Протестировав алгоритм на реальных данных, вы увидите, когда он работает наилучшим образом. Присоединяйтесь к нам и погрузитесь в мир программирования и алгоритмов!
🗓 28 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMl0ID
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 10 правил безопасного Java-приложения:
1️⃣ Валидация на всех уровнях.
📌 Всегда проверяй входные данные (API, формы, запросы к БД). Используй Bean Validation (@Valid
, @NotNull
).
2️⃣ Используй PreparedStatement.
⚠️ Никогда не конкатенируй SQL-запросы напрямую, чтобы избежать SQL-инъекций.
3️⃣ Не раскрывай детали исключений.
📌 Отправляй клиенту общий код ошибки, а детали логируй.
4️⃣ Скрывай конфигурацию.
💡 Используй application.yml
с переменными окружения для чувствительных данных (пароли, ключи API).
5️⃣ Ограничивай доступ.
📌 Spring Security — твой друг. Используй @PreAuthorize
и грамотно настраивай роли и права доступа.
6️⃣ Безопасная сериализация.
⚠️ Избегай Java-стандартной сериализации. Предпочитай JSON (Jackson) с явными DTO.
7️⃣ Используй актуальные версии библиотек.
📌 Регулярно проверяй зависимости (dependency-check
), чтобы избежать известных уязвимостей.
8️⃣ Грамотно логируй.
💡 Не логируй пароли, токены и персональные данные. Используй SLF4J с Logback, настрой уровень логов.
9️⃣ Безопасные куки и заголовки.
📌 Используй атрибуты куки: Secure
, HttpOnly
, SameSite
. Spring Security поможет тебе настроить это быстро.
🔟 Настрой Security Headers.
📌 Используй заголовки:
* X-Frame-Options: DENY
* X-Content-Type-Options: nosniff
* Content-Security-Policy
(CSP)
Безопасность — это не разовая задача, а процесс 💡
👉@BookJava
Spring Framework в деталях
SimpleJdbcInsert - Spring Framework JDBC
АОП в Spring Framework
XML-конфигурация АОП в Spring Framework
Транзакции - Spring Framework в деталях
источник
👉@BookJava
🔐 Основы сжатия данных: создаем RLE архиватор
Приглашаем на открытый урок.
🗓 28 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
На этом вебинаре мы начнем создавать собственный архиватор на Java. Разработаем базовую структуру программы с пользовательским интерфейсом и реализуем алгоритм RLE (кодирование длин серий) для сжатия данных. Изучим как базовую, так и улучшенную версию RLE.
Протестируем эффективность алгоритма на разных типах файлов и увидим, когда этот простой метод сжатия работает наиболее эффективно.
Практическое погружение в мир алгоритмов сжатия данных для всех, кто интересуется программированием и структурами данных.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMenHb
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
💻 Модели межсервисного взаимодействия
Изучите различные модели взаимодействия между микросервисами и выберите оптимальный подход для вашего проекта
Приглашаем на открытый урок.
🗓 28 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Software Architect».
На вебинаре вы узнаете:
✔️ Основные принципы и типы межсервисного взаимодействия.
✔️ Синхронные и асинхронные модели взаимодействия: плюсы и минусы.
✔️ Использование API Gateway и Service Mesh для управления трафиком.
✔️ Паттерны и лучшие практики для надежного и масштабируемого взаимодействия.
✔️ Примеры успешных реализаций межсервисного взаимодействия в реальных проектах.
Вебинар будет полезен:
- Разработчикам, работающим с микросервисной архитектурой.
- Архитекторам ПО, стремящимся оптимизировать межсервисное взаимодействие.
- Backend и Fullstack разработчикам, заинтересованным в улучшении взаимодействия между сервисами.
- DevOps-инженерам, отвечающим за развертывание и управление микросервисами.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - SoftwareArc_06
👉 Регистрация на вебинар: https://vk.cc/cMcrjZ
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Dependency Inversion Principle (D в SOLID)
Зависимости должны идти от высокоуровневой политики к низкоуровневым деталям, а не наоборот. Абстракции — хозяева, реализации — обслуживающий персонал.
📌 Коротко о сути
* Модули верхнего уровня (бизнес-логика) зависят только от интерфейсов/абстракций.
* Модули нижнего уровня (база, сеть, файлы) также зависят от тех же абстракций.
* Сами абстракции не знают ничего о деталях, тем самым разрывая «бетонную» сцепку между слоями.
💡 Мини-пример (Java 17+, Spring Boot 3+)
// 1️⃣ Абстракция — контракт
public interface NotificationSender {
void send(Message msg);
}
// 2️⃣ Верхний уровень — бизнес-служба
@Service
public class BillingService {
private final NotificationSender sender;
public BillingService(NotificationSender sender) {
this.sender = sender; // зависим от контракта
}
public void bill(Client c) {
// ...
sender.send(new Message("Invoice #42"));
}
}
// 3️⃣ Низкий уровень — деталь
@Component
public class EmailSender implements NotificationSender {
public void send(Message msg) {
// SMTP-магия
}
}
final
= явная зависимость.TransferPort
, а не JdbcTransferRepository
).class
. Если появится второй вариант — быстро вынесешь интерфейс (IDE поможет).Три похожих слова в Java — final
, finally
, finalize
— но смысл у них совершенно разный. Разберёмся 🧠
🔒 final
Ключевое слово. Используется для ограничений:
* final class
— нельзя наследовать.
* final method
— нельзя переопределить.
* final variable
— нельзя изменить значение (один раз присвоил — всё).
📌 Особенно важно для immutability и thread-safety.
final int x = 10;
x = 20; // ошибка компиляции
finally
try-catch-finally
. Выполняется всегда, даже если был return
или exception
.
try {
// что-то может выбросить исключение
} catch (Exception e) {
// обработка ошибки
} finally {
// всегда выполнится
}
finalize()
Object
, вызывался перед удалением объекта сборщиком мусора.AutoCloseable
и try-with-resources
.
@Override
protected void finalize() throws Throwable {
System.out.println("До свидания...");
}
final
— про нельзя менятьfinally
— про всегда выполнитсяfinalize
— про устаревший и бесполезный метод👩💻 Хотите научиться эффективно работать с многопоточными приложениями на Java?
На открытом уроке мы разберем потокобезопасные очереди JDK, которые являются обязательной частью многопоточных приложений. Вы узнаете, как они устроены, какие внутренние механизмы лежат в их основе и как правильно их использовать в своих проектах.
🗓 26 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Professional».
Освоив принципы работы потокобезопасных очередей, вы сможете создать более производительные и стабильные многопоточные приложения. Эти знания откроют новые возможности для вашего профессионального роста в Java-разработке.
🔗 Ссылка на регистрацию: https://vk.cc/cM8APp
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
📌 picocli — это современная библиотека для создания CLI-приложений на Java. Она упрощает разработку командных интерфейсов, обеспечивая:
* Автоматическую генерацию --help
и --version
* Поддержку подкоманд (как в git commit
, git push
)
* Аргументы, параметры, опции с короткими и длинными флагами (-v
, --verbose
)
* Интеграцию с GraalVM (подходит для нативной компиляции)
* Поддержку аннотаций (аннотируй POJO — и готово!)
* Автоматическую валидацию аргументов
* Цветной вывод и гибкое форматирование
* Интерактивный режим и автодополнение
Проект активно развивается, полностью документирован и используется в сотнях продакшн-проектов. Если ты ищешь мощную и простую в использовании CLI-библиотеку на Java — picocli отличный выбор.
https://github.com/remkop/picocli
👉@BookJava
Double-brace инициализация в Java — это идиома, которая используется для инициализации коллекций (и иногда других объектов) в краткой форме. Она выглядит как две открывающие фигурные скобки подряд {{
и имеет специфическое поведение. Пример:
import java.util.*;
List<String> list = new ArrayList<String>() {{
add("one");
add("two");
add("three");
}};
new ArrayList<String>() { ... }
ArrayList
.
{{ ... }}
add()
).final
, например:
private static final Set<String> set = new HashSet<>() {{
add("A");
add("B");
}};
Stream
и Collectors
):
List<String> list = Stream.of("one", "two", "three")
.collect(Collectors.toList());
public static List<String> createList() {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
return list;
}
List<String> list = List.of("one", "two", "three");
Set<String> set = Set.of("A", "B");
🧠@Value
vs @ConfigurationProperties
— не выбирай наобум
Оба способа хороши для конфигурации, но используют их по-разному. И если ты всё ещё везде пихаешь @Value
, держи краткий гайд, когда лучше что:
📌 @Value
— просто, но не гибко:
@Value("${my.prop}")
private String value;
TestPropertySource
) @ConfigurationProperties
— сила и масштаб:
@ConfigurationProperties(prefix = "app.feature")
public class FeatureProperties {
private boolean enabled;
private List<String> items;
}
@EnableConfigurationProperties
или аннотируй как @Component
@Validated
)/actuator/configprops
)@Value
внутрь @ConfigurationProperties
— это антипаттерн.@Value
норм. Но как только появляется структура, коллекции, логика — всегда используй @ConfigurationProperties
.🔥 Java и производительность — новая тема от онлайн-конференции Podlodka Java Crew
С 26 по 30 мая вас ждет тематическая неделя, посвящённая продвинутым практикам оптимизации Java-приложений.
В программе:
— Доклад Владимира Плизги (Tibbo System) о подходах к профилированию и инструментах, которые действительно работают;
— Воркшоп по JMH от Григория Кошелева (Контур) — научитесь писать микробенчмарки правильно;
— Круглый стол с Антоном Курако (Т-Банк) и Михаилом Поливахой (Spring АйО) — сравнение Spring, Micronaut, Quarkus и Kora через призму производительности;
— Опыт команды НСПК по нагрузочному тестированию в бою — расскажет Павел Митин.
А ещё — JFR, корутины, Kubernetes и десятки инсайтов из продакшена.
🎯 Неделя для тех, кто держит перформанс под контролем.
🔗 Подключайся: podlodka.io/javacrew
🧠 Сейчас расскажу о распределённых транзакциях в Java и почему их стоит использовать с осторожностью.
Когда твое приложение работает с несколькими источниками данных (например, двумя базами или базой + очередью), часто появляется соблазн обернуть всё в одну большую транзакцию с помощью JTA (Java Transaction API) и XA-ресурсов. Но на практике это почти всегда антипаттерн.
📌 Почему?
* XA-транзакции медленные и сложно отлаживаются.
* Они ломают горизонтальное масштабирование: один сбой — откат по всем участникам.
* Могут приводить к блокировкам и deadlock-ам, если инфраструктура нестабильна.
💡 Современные альтернативы:
1. Transaction outbox pattern — сначала пишем событие/задачу в outbox-таблицу в той же БД, где меняем данные, и только потом публикуем во внешний сервис.
2. Event sourcing — строим архитектуру так, чтобы любые изменения происходили через событийную модель (и проще компенсировать сбои).
3. Idempotency & retries — делаем операции идемпотентными и безопасными к повтору.
⚠️ Не используешь JTA без реальной необходимости. Spring Boot 3+ по умолчанию уже не настраивает JTA — и правильно делает.
Если всё же нужен XA:
* Используй Atomikos, Narayana или Bitronix — это современные провайдеры.
* Но всегда сначала подумай: "Могу ли я упростить архитектуру и уйти от XA?"
Пример из кода на Spring Boot:
// НЕ рекомендуется:
@EnableTransactionManagement
public class Config {
@Bean
public JtaTransactionManager transactionManager() {
return new JtaTransactionManager();
}
}
// Лучше outbox + отдельный publisher
Ищем Java-разработчика в команду онлайн-рекомендаций AI VK 🤖
Будем вместе разрабатывать высоконагруженные микросервисы на Java. Кроме кода доверим коллеге принимать архитектурные и технические решения, гибко настраивать ML-эксперименты и рекомендательный пайплайн.
Если любите технически сложные задачи и хотите работать с большими данными, ждём ваше резюме на сайте VK Team!
Сегодня покажу вам интересный костыль, который до сих пор живёт в JDK и влияет на сортировку списков.
🧠 Если ты когда-нибудь пробовал сортировать LinkedList через Collections.sort()
, знай — под капотом происходит скрытая магия.
📌 Collections.sort(list)
сначала проверяет, какой это список. Если это RandomAccess-list (например, ArrayList) — используется быстрый сортировщик (TimSort) прямо по массиву.
⚠️ Но если это LinkedList (или любой не-рандом-доступ список), внутри происходит... преобразование списка в массив! Сначала все элементы копируются в массив, потом сортируются, а затем результат обратно заливается в твой список.
// Да, это реально работает так:
List<Integer> list = new LinkedList<>();
// ...заполняем...
Collections.sort(list);
Двоичная Java: CDS, CRaC и AOT для ускорения запуска и прогрева JVM
В этой статье вы узнаете о повышении перфоманса Java-приложений. Разберём:
🔹Как работает HotSpot.
🔹Процессы исполнения байт-кода и в чём же основные проблемы.
🔹Технологии для повышения перформанса: CDS, CRaC и AOT.
🔹Два мира сборки приложений: статический и динамический.
Поговорим о двух болях, которые есть в Java-приложениях — это время запуска и время достижения пиковой производительности.
https://habr.com/ru/companies/axiomjdk/articles/911568/
👉@BookJava
🔥 Хардкорный тест для разработчиков, тимлидов и архитекторов!
💻 Ответьте на 11 вопросов и узнайте, достаточно ли у вас знаний, чтобы пройти онлайн-курс «Software Architect» в OTUS по спец.цене.
🦾 Курс поможет прокачать весь арсенал навыков, необходимых архитектору ПО.
❇️ Пройти тест - https://vk.cc/cMj0Ns
💣 Знание продвинутых техник построения архитектуры — это топ-компетенции для программистов в 2025 году. За 4 месяца обучения вы изучите тактики работы с атрибутами качества и архитектурные решения, а также узнаете, как проектировать архитектуру мобильных приложений, микросервисов, баз данных и ML архитектуру пайплайнов.
🎁 Для получения спец.цены используйте промокод, который дает скидку на обучение - SoftwareArc_06
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Проверяешь аргументы вручную? Используй мощь системы типов Java!
📌 Ситуация: часто в методах видишь явные проверки вроде:
public void process(User user) {
if (user == null || user.getName() == null) {
throw new IllegalArgumentException("User или его имя не могут быть null");
}
// код обработки...
}
import java.util.Objects;
public void process(User user) {
Objects.requireNonNull(user, "User не должен быть null");
Objects.requireNonNull(user.getName(), "Имя пользователя не должно быть null");
// код обработки...
}
import lombok.NonNull;
public void process(@NonNull User user) {
// Lombok автоматически вставит проверку на null
}
📌 Обход коллекций в Java: делаем правильно!
Часто вижу, как разработчики по привычке используют старый подход с циклом for
. Сегодня напомню оптимальные способы перебора коллекций в Java.
🧠 1. Foreach (Java 5+)
Коротко, ясно, подходит для большинства задач:
for (String item : collection) {
// обработка элемента
}
collection.forEach(item -> {
// обработка элемента
});
collection.stream()
.filter(Objects::nonNull)
.map(String::toUpperCase)
.forEach(System.out::println);
collection.forEach()
.📌 Stream API: забудьте про peek()
Метод peek()
в Java Stream API выглядит удобным для отладки, но его использование в реальном коде часто приводит к проблемам.
⚠️ Почему не стоит полагаться на peek()?
* peek()
— промежуточная операция, которая не гарантирует вызов функции для каждого элемента стрима.
* Поведение зависит от терминальной операции: например, при некоторых оптимизациях JDK (особенно в параллельных стримах) вызов peek()
может быть пропущен или работать непредсказуемо.
💡 Пример опасного кода:
List<String> names = Stream.of("Java", "Spring", "Hibernate")
.peek(System.out::println) // 👈 анти-паттерн!
.collect(Collectors.toList());
peek()
исключительно для дебага, но никогда — для изменения состояния или важных операций.
List<String> names = Stream.of("Java", "Spring", "Hibernate")
.map(name -> {
log.info("Processing: {}", name); // или другая логика
return name;
})
.collect(Collectors.toList());
peek()
.Ищете эффективные инструменты для создания DSL?
Узнайте, как Kotlin может упростить разработку с помощью JsonBuilder!
Приглашаем на открытый урок.
🗓 22 мая в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Kotlin Backend Developer. Professional».
На открытом уроке вы разберете, как Kotlin позволяет создавать DSL (Domain-Specific Languages), оптимизируя процесс разработки. Мы покажем теорию и практику создания DSL на примере JsonBuilder.
Вы не только научитесь создавать собственные DSL, но и освоите замыкания и extension-методы Kotlin, которые дадут вам дополнительные преимущества при написании чистого и гибкого кода.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Kotlin5
👉 Регистрация на вебинар: https://vk.cc/cMaDZP
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🧠 Ленивая инициализация через Supplier
— элегантная альтернатива double-checked locking
Когда нужно отложить создание тяжёлого объекта до первого обращения, многие вспоминают double-checked locking:
private volatile SomeHeavyObject obj;
public SomeHeavyObject getObj() {
if (obj == null) {
synchronized (this) {
if (obj == null) {
obj = new SomeHeavyObject();
}
}
}
return obj;
}
Supplier
с ленивой инициализацией:
private final Supplier<SomeHeavyObject> lazyObj = Suppliers.memoize(SomeHeavyObject::new);
public SomeHeavyObject getObj() {
return lazyObj.get();
}
Suppliers.memoize
— из Guava. Он гарантирует потокобезопасную инициализацию один раз при первом вызове get()
.AtomicReference
и updateAndGet
, но это уже длиннее и менее выразительно.@Lazy
. Но вне Spring, в обычных Java-приложениях или утилитах — Supplier
с memoize()
идеален.Что такое механизм try-with-resources?
Механизм try-with-resources
в Java — это конструкция, которая упрощает работу с ресурсами, требующими закрытия (например, файлы, сокеты, соединения с БД и т.д.). Он автоматически закрывает ресурсы после завершения блока try
, избавляя от необходимости писать finally
вручную.
📌 Поддерживается с Java 7
📌 Ресурсы должны реализовывать интерфейс AutoCloseable
💡 Пример:
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// reader будет закрыт автоматически, даже если произойдёт исключение
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) {
System.out.println(reader.readLine());
}
👩💻 Как использовать SpEL в приложениях на Spring?
Присоединяйтесь к открытому уроку «SpELые приложения на Spring» и узнайте, как динамически выражать и обрабатывать данные в Spring-приложениях.
SpEL (Spring Expression Language) — это мощный инструмент для внедрения динамических выражений, который широко применяется в проектировании приложений на Spring.
🗓 21 мая в 19:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».
Что вас ждёт:
✔️ Погружение в SpEL и его возможности.
✔️ Применение SpEL в реальных Spring-проектах.
✔️ Понимание, как Spring использует SpEL для обработки и динамических вычислений.
Урок для Spring-разработчиков, Java-бэкенд-инженеров и архитекторов ПО.
🔗 Ссылка на регистрацию: https://vk.cc/cM6W9C
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
🚀 Открой для себя идеальный путь к лидерству с карьерным тестом от ОЭЗ «Алабуга»! 🌟
Мечтаете о карьере в крупной компании, где ваш потенциал раскроется на полную? Наш тест поможет вам определить вашу уникальную лидерскую роль. Может быть, именно вы станете тем лидером, который выведет команду на новый уровень?
После прохождения теста вы можете заполнить заявку и получить приглашение на эксклюзивную лидерскую программу. Участие в программе открывает реальные перспективы трудоустройства в ОЭЗ «Алабуга», предоставляя шанс начать путь к профессиональному признанию.
Сделайте первый шаг к своему будущему сегодня! Пройдите тест, подайте заявку и начните строить свою карьеру вместе с нами. 🎯
🧠 Трюк с @EventListener
в Spring — убираем лишний @TransactionalEventListener
Когда тебе нужно обрабатывать события в рамках транзакции, мы часто пишем:
@TransactionalEventListener
public void handleEvent(MyEvent event) {
// ...
}
@TransactionalEventListener
по умолчанию срабатывает после коммита. Иногда это не очевидно и вызывает баги, особенно если ожидаешь, что событие обработается внутри транзакции.@EventListener
, но вместе с TransactionSynchronizationManager
.
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
if (TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// обработка события после коммита
}
}
);
} else {
// fallback: нет активной транзакции — выполняем сразу
}
}
}
🧠 Как не словить LazyInitializationException
в Spring Boot + Hibernate
Одна из самых частых ошибок при работе с JPA:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection
LAZY
) обращается к БД вне транзакции — например, в слое контроллера или после закрытия Session
.@Transactional
в сервисе@Transactional
:
@Transactional
public UserDto getUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow();
// OK: коллекция friends будет инициализирована в транзакции
return new UserDto(user.getName(), user.getFriends());
}
@Transactional
в контроллерах — это плохая практика.JOIN FETCH
:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.friends WHERE u.id = :id")
Optional<User> findByIdWithFriends(@Param("id") Long id);
@Query("""
SELECT new com.example.UserDto(u.name, f.name)
FROM User u
LEFT JOIN u.friends f
WHERE u.id = :id
""")
List<UserDto> findUserWithFriendNames(@Param("id") Long id);
fetch join
— ваши лучшие друзья, если нужен контроль и производительность.