Библиотека Java разработчика
10.8K subscribers
1.14K photos
564 videos
58 files
1.44K links
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.


По всем вопросам @evgenycarter

РКН clck.ru/3KoGeP
Download Telegram
🧠 JPA Batch Insert: ускоряем и защищаем от OOM

📌 Настройка 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 для 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 июня, чтобы попасть в команду за выходные!

Подробности — на сайте.
💩4👍2
🧠 Коллекторы и 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
👍51
🧠 Осторожно с @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
👍93
📚 Продвинутые методы архивации: LZ77/78

Приглашаем на открытый урок.

🗓 25 июня в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Алгоритмы и структуры данных».

✔️ На этом вебинаре мы завершим создание архиватора, добавив алгоритм LZ77/78. Разберем принцип словарного сжатия, механизм поиска повторяющихся последовательностей и формат их кодирования.

✔️ Имплементируем выбранный алгоритм и проведем финальное сравнение всех трех методов сжатия (RLE, Huffman, LZ77/78). Определим, какие алгоритмы лучше работают для различных типов файлов и почему.

Завершающее практическое занятие для тех, кто хочет освоить продвинутые алгоритмы и увидеть их применение в реальном проекте.​​​​​​​​​​​​​​​​

🎁 Всем участникам вебинара дарим промокод, который дает скидку на обучение - Algo5

👉 Регистрация на вебинар: https://vk.cc/cMXtZN

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Вопросы-ответы собеседования

Можно ли создать экземпляр абстрактного класса?
Что такое интерфейс?
Как вызвать нестатический метод в статическом?
Чем отличаются параметры от аргументов в методе?
Что такое конструктор? Как его создать и вызвать?
Что такое параметризованный конструктор?
Что такое конструктор по умолчанию?
Что такое приватный конструктор? Зачем он закрытый?
Что такое статическая переменная? Как работает static поле?
Что такое статический метод? Как вызвать static метод?

источник

👉@BookJava
👍71
💡 Почему в 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Трюк с @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
Совет 💡

Если вы хотите получить сообщение о первопричине, вы можете легко и безопасно получить его с помощью Apache Commons ExceptionUtils. Методы getRootCauseMessage(Exception ex) выдают сообщение в виде {ClassNameWithoutPackage} {ThrowableMessage}

👉@BookJava
👍7💩1