🧠 JPA Batch Insert: ускоряем и защищаем от OOM
📌 Настройка Hibernate
Добавьте в
Это позволит драйверу посылать пачками, а Hibernate — сортировать операции для максимальной эффективности.
💡 Сниппет для batch-пакетов
–
–
⚠️ Важные моменты
* GenerationType.IDENTITY отключает batching. Используйте
* При двусторонних связях (OneToMany) избегайте каскадного сохранения огромных графов — лучше сохранять “плоско” и затем связывать.
* Следите за JDBC-драйвером: не все поддерживают batch-вставки одинаково хорошо.
💡 Совет по мониторингу
Запустите приложение с
📌 Результат
* Скорость записи растёт в 5–10× (в зависимости от нагрузки).
* Память на стороне приложения остаётся стабильной, без роста Persistence Context.
👉@BookJava
📌 Настройка Hibernate
Добавьте в
application.yml
или properties
:
spring:
jpa:
properties:
hibernate.jdbc.batch_size: 50 # размер пакета
hibernate.order_inserts: true # группировка INSERT’ов
hibernate.order_updates: true # группировка UPDATE’ов
Это позволит драйверу посылать пачками, а Hibernate — сортировать операции для максимальной эффективности.
💡 Сниппет для batch-пакетов
@Service
@RequiredArgsConstructor
public class OrderService {
private final EntityManager em;
private static final int BATCH_SIZE = 50;
@Transactional
public void saveAll(List<Order> orders) {
for (int i = 0; i < orders.size(); i++) {
em.persist(orders.get(i));
if (i > 0 && i % BATCH_SIZE == 0) {
em.flush();
em.clear(); // освобождаем persistence-context
}
}
em.flush();
em.clear();
}
}
–
flush()
выталкивает пакеты в БД,–
clear()
освобождает ОЗУ от управляемых сущностей.⚠️ Важные моменты
* GenerationType.IDENTITY отключает batching. Используйте
@SequenceGenerator
с allocationSize
.* При двусторонних связях (OneToMany) избегайте каскадного сохранения огромных графов — лучше сохранять “плоско” и затем связывать.
* Следите за JDBC-драйвером: не все поддерживают batch-вставки одинаково хорошо.
💡 Совет по мониторингу
Запустите приложение с
-Dorg.hibernate.SQL=DEBUG
и -Dhibernate.format_sql=true
— вы увидите групповые INSERT
вместо множества одиночных.📌 Результат
* Скорость записи растёт в 5–10× (в зависимости от нагрузки).
* Память на стороне приложения остаётся стабильной, без роста Persistence Context.
👉@BookJava
👍6
🧠 Record (Java 16+) + pattern matching для
📌 Запись DTO с валидацией через компактный конструктор:
– автоматические
🧠 Проверка и приведение типов в одном выражении:
– нет лишних кастов, код чище и безопаснее.
💡 Совет: для полей — коллекций или массивов — избегайте поверхностной мутабельности:
– таким образом
⚠️ Антипаттерн: не используйте
👉@BookJava
instanceof
(Java 14+) в Java 17+ позволяют писать лаконичный и безопасный код:📌 Запись DTO с валидацией через компактный конструктор:
public record User(String name, String email) {
public User {
Objects.requireNonNull(name, "name не должен быть null");
if (!email.contains("@")) {
throw new IllegalArgumentException("Неверный email: " + email);
}
}
}
– автоматические
toString()
, equals()
, hashCode()
без лишнего кода.🧠 Проверка и приведение типов в одном выражении:
Object obj = …;
if (obj instanceof User u) {
System.out.println("Привет, " + u.name());
}
– нет лишних кастов, код чище и безопаснее.
💡 Совет: для полей — коллекций или массивов — избегайте поверхностной мутабельности:
public record Team(String name, List<String> members) {
public Team {
members = List.copyOf(members);
}
}
– таким образом
members
нельзя изменить извне.⚠️ Антипаттерн: не используйте
public record X(List<String> list)
без копирования — рискуете нарушить неизменяемость!👉@BookJava
👍5
VK Weekend Offer: отправьте заявку, пройдите интервью и получите офер!
28–29 июня VK проведёт Weekend Offer для бэкендеров с опытом от трёх лет. Участников со знанием Java, Go, Python или C++ ждут технические собеседования, знакомство с продуктами и, если всё сложится, офер уже в конце выходных.
Ребята много лет создают облачные решения, системы рекомендаций и поисковые движки — всё с миллионами пользователей в проде — и сейчас ищут новых коллег. Поэтому оставляйте заявку до 25 июня, чтобы попасть в команду за выходные!
Подробности — на сайте.
28–29 июня VK проведёт Weekend Offer для бэкендеров с опытом от трёх лет. Участников со знанием Java, Go, Python или C++ ждут технические собеседования, знакомство с продуктами и, если всё сложится, офер уже в конце выходных.
Ребята много лет создают облачные решения, системы рекомендаций и поисковые движки — всё с миллионами пользователей в проде — и сейчас ищут новых коллег. Поэтому оставляйте заявку до 25 июня, чтобы попасть в команду за выходные!
Подробности — на сайте.
💩4👍2
🧠 Коллекторы и
Да, но с нюансами.
📌 Короткий ответ:
Если ты используешь Java 16+, можешь заменить:
на:
💡 Но будь осторожен. Вот 3 ключевых отличия:
1️⃣ Немодифицируемость
*
*
2️⃣ Тип возвращаемого списка
*
*
Если ты делаешь что-то вроде:
то поведение может измениться.
3️⃣ Параллельные стримы
⚠️ Когда НЕ стоит заменять:
* Если ты мутируешь список после получения.
* Если ты полагаешься на конкретный тип (например,
* Если ты используешь Java < 16.
✅ Когда заменить можно:
* Если тебе нужен read-only список.
* Если ты не изменяешь коллекцию.
* Если важна сжатость и выразительность.
📌 Резюме:
👉@BookJava
toList()
в Java 16+: можно ли заменить collect(Collectors.toList())
на просто .toList()
?Да, но с нюансами.
📌 Короткий ответ:
Если ты используешь Java 16+, можешь заменить:
List<String> list = stream.collect(Collectors.toList());
на:
List<String> list = stream.toList();
💡 Но будь осторожен. Вот 3 ключевых отличия:
1️⃣ Немодифицируемость
*
.toList()
возвращает немодифицируемый список (immutable).*
Collectors.toList()
возвращает modifiable ArrayList.
var list1 = List.of("a", "b");
var list2 = list1.stream().toList();
list2.add("c"); // 💥 UnsupportedOperationException
var list3 = list1.stream().collect(Collectors.toList());
list3.add("c"); // ✅ OK
2️⃣ Тип возвращаемого списка
*
Collectors.toList()
— это ArrayList (или его сабкласс).*
.toList()
— это неопределённый тип внутри JDK (часто List.of
под капотом).Если ты делаешь что-то вроде:
if (list instanceof ArrayList) ...
то поведение может измениться.
3️⃣ Параллельные стримы
.toList()
оптимизирован для параллельных стримов: может работать быстрее, но также может повлиять на порядок, если ты этого явно не контролируешь.⚠️ Когда НЕ стоит заменять:
* Если ты мутируешь список после получения.
* Если ты полагаешься на конкретный тип (например,
ArrayList
).* Если ты используешь Java < 16.
✅ Когда заменить можно:
* Если тебе нужен read-only список.
* Если ты не изменяешь коллекцию.
* Если важна сжатость и выразительность.
📌 Резюме:
Заменяйcollect(Collectors.toList())
на.toList()
, только если тебе действительно не нужен изменяемый список. Это безопасно при соблюдении условий, но может привести к неожиданным багам в тестах и проде, если забыть про неизменяемость.
👉@BookJava
👍5❤1
🧠 Осторожно с
Очень частый анти-паттерн, который легко упустить 👇
Кажется, всё ок. Но ❌ транзакция НЕ работает.
📌 Почему?
Spring AOP использует прокси, а прокси не “видит” вызовы private-методов внутри класса. Такие вызовы происходят напрямую, мимо прокси-обёртки — и аннотация
💡 Решение:
1. Сделай метод public и вызывай его извне (или из другого бина).
2. Или выдели этот метод в отдельный бин-сервис.
Пример:
И в UserService:
⚠️ Так же не работают protected, private, final, static, и
Нужно помнить: Spring AOP = прокси, а значит, работают только публичные методы, вызываемые ИЗВНЕ.
👉@BookJava
@Transactional
на private-методах!Очень частый анти-паттерн, который легко упустить 👇
@Service
public class UserService {
@Transactional
private void saveUser(User user) {
userRepository.save(user);
}
public void create() {
saveUser(new User());
}
}
Кажется, всё ок. Но ❌ транзакция НЕ работает.
📌 Почему?
Spring AOP использует прокси, а прокси не “видит” вызовы private-методов внутри класса. Такие вызовы происходят напрямую, мимо прокси-обёртки — и аннотация
@Transactional
просто игнорируется.💡 Решение:
1. Сделай метод public и вызывай его извне (или из другого бина).
2. Или выдели этот метод в отдельный бин-сервис.
Пример:
@Service
public class UserTransactionalHelper {
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
}
И в UserService:
public void create() {
helper.saveUser(new User());
}
⚠️ Так же не работают protected, private, final, static, и
@PostConstruct
-методы.Нужно помнить: Spring AOP = прокси, а значит, работают только публичные методы, вызываемые ИЗВНЕ.
👉@BookJava
👍9❤3
📚 Продвинутые методы архивации: LZ77/78
Приглашаем на открытый урок.
🗓 25 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
✔️ На этом вебинаре мы завершим создание архиватора, добавив алгоритм LZ77/78. Разберем принцип словарного сжатия, механизм поиска повторяющихся последовательностей и формат их кодирования.
✔️ Имплементируем выбранный алгоритм и проведем финальное сравнение всех трех методов сжатия (RLE, Huffman, LZ77/78). Определим, какие алгоритмы лучше работают для различных типов файлов и почему.
Завершающее практическое занятие для тех, кто хочет освоить продвинутые алгоритмы и увидеть их применение в реальном проекте.
🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5
👉 Регистрация на вебинар: https://vk.cc/cMXtZN
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Приглашаем на открытый урок.
🗓 25 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».
Завершающее практическое занятие для тех, кто хочет освоить продвинутые алгоритмы и увидеть их применение в реальном проекте.
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
Вопросы-ответы собеседования
Можно ли создать экземпляр абстрактного класса?
Что такое интерфейс?
Как вызвать нестатический метод в статическом?
Чем отличаются параметры от аргументов в методе?
Что такое конструктор? Как его создать и вызвать?
Что такое параметризованный конструктор?
Что такое конструктор по умолчанию?
Что такое приватный конструктор? Зачем он закрытый?
Что такое статическая переменная? Как работает static поле?
Что такое статический метод? Как вызвать static метод?
источник
👉@BookJava
Можно ли создать экземпляр абстрактного класса?
Что такое интерфейс?
Как вызвать нестатический метод в статическом?
Чем отличаются параметры от аргументов в методе?
Что такое конструктор? Как его создать и вызвать?
Что такое параметризованный конструктор?
Что такое конструктор по умолчанию?
Что такое приватный конструктор? Зачем он закрытый?
Что такое статическая переменная? Как работает static поле?
Что такое статический метод? Как вызвать static метод?
источник
👉@BookJava
👍7❤1
💡 Почему в
Когда используешь
1. ❌ Нельзя добавлять дубликаты
2. ❌ Нельзя добавлять
Разбираемся, почему так:
🔐
Метод
* После создания ты не можешь изменить его (добавить, удалить элемент).
* Все элементы внутри должны быть уникальны и не должны быть
📛 Почему нельзя дубликаты?
Потому что
А
Это сделано для того, чтобы не было тихих ошибок — чтобы ты сразу увидел, что передал неуникальные значения.
🕳️ Почему нельзя
Это сделано сознательно —
👀 Хочешь изменяемый
👉@BookJava
Set.of()
в Java нельзя добавить дубликаты и null
?Когда используешь
Set.of(...)
, можно столкнуться с двумя неожиданностями:1. ❌ Нельзя добавлять дубликаты
2. ❌ Нельзя добавлять
null
Разбираемся, почему так:
🔐
Set.of(...)
— это immutable SetМетод
Set.of(...)
, добавленный в Java 9, создаёт неизменяемое множество. Это значит:* После создания ты не можешь изменить его (добавить, удалить элемент).
* Все элементы внутри должны быть уникальны и не должны быть
null
.📛 Почему нельзя дубликаты?
Потому что
Set
по определению — это коллекция уникальных элементов.А
Set.of(...)
бросает IllegalArgumentException
, сразу во время создания, если переданы дубликаты:
Set.of("a", "b", "a"); // 💥 Бросит исключение!
Это сделано для того, чтобы не было тихих ошибок — чтобы ты сразу увидел, что передал неуникальные значения.
🕳️ Почему нельзя
null
?Set.of(...)
не принимает null
, потому что он реализован через внутренние immutable структуры, которые не допускают null`-значений. При попытке добавить `null
получишь NullPointerException
.
Set.of("a", null); // 💥 NullPointerException
Это сделано сознательно —
null
может вести к неочевидным багам и плохо сочетается с концепцией неизменяемых коллекций.👀 Хочешь изменяемый
Set
, который принимает null
и дубликаты фильтрует сам? Используй HashSet
:
Set<String> set = new HashSet<>();
set.add(null); // ✅ Можно
👉@BookJava
👍4🤔3
🤯 🤯 🤯 Параллелизм в многопоточном Java-коде создаёт новые проблемы в тестировании, а баги остаются незамеченными?
⚡️ Приглашаем на открытый вебинар «Юнит тесты для многопоточного кода»
24 июня в 20:00 МСК.
На вебинаре мы разберём:
✔️ Как обнаружить гонки, дедлоки и нестабильность в многопоточном коде.
✔️ Как использовать argumentCaptor и spy для проверки взаимодействия потоков.
✔️ Эмуляцию задержек и таймингов с помощью AdditionalAnswers.
🦾 После урока вы будете уверенно писать стабильные unit-тесты для многопоточного кода, выявлять скрытые баги и улучшать качество тестирования.
Открытый урок проходит в преддверии старта курса «Java Developer. Advanced».
Все участники получат скидку на обучение.
🔗 Регистрируйтесь прямо сейчас: https://vk.cc/cMZNzb
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
24 июня в 20:00 МСК.
На вебинаре мы разберём:
Открытый урок проходит в преддверии старта курса «Java Developer. Advanced».
Все участники получат скидку на обучение.
🔗 Регистрируйтесь прямо сейчас: https://vk.cc/cMZNzb
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Трюк с
Когда ты используешь
📌 Пример:
Если
⚠️ Это особенно критично, если ты триггеришь событие в
💡 Как избежать:
1. Используй явное управление порядком инициализации через
2. Не публикуй события слишком рано — лучше в
🧵 Альтернатива: если тебе нужно гарантированное выполнение в момент старта — лучше использовать
👉@BookJava
@EventListener
в Spring Boot — неочевидная ловушкаКогда ты используешь
@EventListener
для обработки событий в Spring, ты можешь попасть в баг, который очень сложно отловить в бою — пропущенные события.📌 Пример:
@Component
public class MyListener {
@EventListener
public void on(MyEvent event) {
// логика
}
}
Если
MyListener
бин ещё не проинициализирован, а событие уже публикуется (ApplicationEventPublisher#publishEvent
), то метод on
просто не будет вызван. Spring не будет "накапливать" события для будущих слушателей.⚠️ Это особенно критично, если ты триггеришь событие в
@PostConstruct
или в CommandLineRunner
, а слушатель находится в другом бине, который ещё не загружен.💡 Как избежать:
1. Используй явное управление порядком инициализации через
@DependsOn
.2. Не публикуй события слишком рано — лучше в
ApplicationReadyEvent
:
@Component
public class Publisher {
private final ApplicationEventPublisher publisher;
public Publisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
publisher.publishEvent(new MyEvent(this));
}
}
🧵 Альтернатива: если тебе нужно гарантированное выполнение в момент старта — лучше использовать
ApplicationRunner
или InitializingBean
, где порядок можно контролировать проще.👉@BookJava
👍6
Совет 💡
Если вы хотите получить сообщение о первопричине, вы можете легко и безопасно получить его с помощью
👉@BookJava
Если вы хотите получить сообщение о первопричине, вы можете легко и безопасно получить его с помощью
Apache Commons ExceptionUtils
. Методы getRootCauseMessage(Exception ex)
выдают сообщение в виде {ClassNameWithoutPackage} {ThrowableMessage}
👉@BookJava
👍7💩1