uk
Feedback
Библиотека Java разработчика

Библиотека Java разработчика

Відкрити в Telegram

📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

Показати більше

📈 Аналітичний огляд Telegram-каналу Библиотека Java разработчика

Канал Библиотека Java разработчика (@bookjava) у мовному сегменті Російська є активним учасником. На даний момент спільнота об'єднує 10 115 підписників, посідаючи 11 697 місце в категорії Технології та додатки та 62 901 місце у регіоні Росія.

📊 Показники аудиторії та динаміка

З моменту свого створення невідомо, проект продемонстрував стрімке зростання, зібравши аудиторію у 10 115 підписників.

За останніми даними від 27 серпня, 2026, канал демонструє стабільну активність. Хоча за останні 30 днів спостерігається зміна кількості учасників на -73, а за останні 24 години на -3, загальне охоплення залишається високим.

  • Статус верифікації: Не верифікований
  • Рівень залученості (ER): Середній показник залученості аудиторії становить 7.20%. Протягом перших 24 годин після публікації контент зазвичай збирає 3.71% реакцій від загальної кількості підписників.
  • Охоплення публікацій: В середньому кожен допис отримує 729 переглядів. Протягом першої доби публікація в середньому набирає 375 переглядів.
  • Реакції та взаємодія: Аудиторія активно підтримує контент: середня кількість реакцій на один пост – 7.
  • Тематичні інтереси: Контент зосереджений навколо ключових тем, таких як string, интерфейс, строка, boot, api.

📝 Опис та контентна політика

Автор описує ресурс як майданчик для висловлення суб'єктивної думки:
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

Завдяки високій частоті оновлень (останні дані отримано 28 серпня, 2026), канал підтримує актуальність та високий рівень охоплення публікацій. Аналітика показує, що аудиторія активно взаємодіє з контентом, що робить його важливою точкою впливу в категорії Технології та додатки.

10 115
Підписники
-324 години
-167 днів
-7330 день
Архів дописів
🧠 Нюанс с Optional.map() и методами, возвращающими Optional Многие Java-разработчики на автомате пишут что-то вроде: Optiona
🧠 Нюанс с 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>). 👉@BookJava

🧠 ExecutorService vs Virtual Threads: подводный камень с shutdownNow() Java 21 принес Virtual Threads (Preview), и всё чаще
🧠 ExecutorService vs Virtual Threads: подводный камень с shutdownNow() Java 21 принес Virtual Threads (Preview), и всё чаще появляется соблазн запускать их через ExecutorService. Но вот что важно помнить: 📌 shutdownNow() — опасная ловушка при работе с виртуальными потоками.

ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

Future<?> future = executor.submit(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        System.out.println("Interrupted!");
    }
});

executor.shutdownNow(); // ❗️Ничего не произойдёт
💡 Почему? Метод shutdownNow() не может прервать виртуальные потоки, если они запущены через Executors.newVirtualThreadPerTaskExecutor(). Он лишь помечает пул как завершённый и возвращает список задач, которые ещё не стартовали. Уже запущенные виртуальные потоки продолжают выполняться — Thread.interrupt() не работает, потому что у виртуальных потоков отсутствует связь с ThreadGroup, к которому привязан shutdownNow. ⚠️ Следствие: Если вы рассчитываете, что shutdownNow() "остановит всё" — вы можете получить утечку задач или зависания. 🔧 Что делать? 1. Контролируйте завершение через Future.cancel(true) — он вызывает interrupt() на конкретной задаче. 2. Стройте явную кооперативную модель отмены — с флагами или Thread.interrupted() внутри задачи. 3. Для массовой отмены — храните Future задач и отменяйте вручную. 👉@BookJava

🧠 Знаешь ли ты, что @Transactional на private - методах не работает? Да, Spring просто не применяет прокси к private-методам
🧠 Знаешь ли ты, что @Transactional на private - методах не работает? Да, Spring просто не применяет прокси к private-методам. Это частый баг, который трудно отловить: ты вызываешь приватный метод внутри бина, а транзакция… не начинается 🤷‍♂️ 📌 Почему так происходит? Spring AOP по умолчанию использует динамические прокси (JDK или CGLIB), которые перехватывают внешние вызовы. А вызов private - метода из того же класса — это внутренний вызов, который обходит прокси. Пример, который НЕ работает:

@Service
public class UserService {

    public void createUser() {
        saveUser(); // Вызов мимо прокси 😞
    }

    @Transactional
    private void saveUser() {
        // Транзакция НЕ начнется!
    }
}
💡 Как правильно: 1. Сделай метод public или хотя бы protected, 2. Или выноси в отдельный бин:

@Service
public class UserService {

    private final TxUserSaver txUserSaver;

    public UserService(TxUserSaver txUserSaver) {
        this.txUserSaver = txUserSaver;
    }

    public void createUser() {
        txUserSaver.saveUser(); // Теперь через прокси ✅
    }
}

@Service
public class TxUserSaver {

    @Transactional
    public void saveUser() {
        // Всё сработает как надо
    }
}
⚠️ Проверь свои сервисы — ты можешь удивиться, сколько транзакций у тебя не работают. Особенно в проектах, где @Transactional ставят "на всякий случай". 👉@BookJava

⚡️ Квиз на знание Java Пройти тестирование — сложно! А ты справишься? 21 вопрос, 30 минут Проверь себя - пройди квиз и оцени
⚡️ Квиз на знание Java Пройти тестирование — сложно! А ты справишься? 21 вопрос, 30 минут Проверь себя - пройди квиз и оцени свой уровень навыков, а также свою готовность к обучению на курсе — «Разработчик на Spring Framework» от OTUS. 💻 За 5 месяцев обучения ты освоишь современные возможности Spring, научишься быстро проходить путь от идеи до production-grade, создавать Web-приложения на микросервисной архитектуре и решать высокоуровневые задачи по разработке. 👉 ПРОЙТИ ТЕСТ: https://vk.cc/cLshxc Если успешно пройдешь тест, сможешь забронировать место в группе по выгодной цене! И еще дарим промокод SPRING5 Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

+7
Java. Сортировки Java. Сортировка пузырьком. Java. О сортировке выбором. Java. Быстрая сортировка. Объяснение на пальцах) Java. Оценка сложности алгоритмов сортировки. Java. Сортировка слиянием. Java. Сортировка подсчетом. Java. Сортировка вставками. Java. Сортировка расческой. От пузырька до расчески. 👉@BookJava

🧠 Как в Java сломать equals() и потерять данные в HashMap Сегодня покажу, как неочевидный баг в equals()/hashCode() может пр
🧠 Как в 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) {}
👉@BookJava

🤖 А ты справишься с тестом по Kotlin? 🏆 Пройди тест из 10 вопросов, проверь свой уровень знаний и получи скидку на онлайн-к
🤖 А ты справишься с тестом по Kotlin? 🏆 Пройди тест из 10 вопросов, проверь свой уровень знаний и получи скидку на онлайн-курс «Kotlin Backend Developer. Professional» от OTUS! Если успешно пройдешь тест, сможешь забронировать место в группе по выгодной цене! И еще дарим промокод Kotlin5 🎫 Курс можно приобрести в рассрочку ➡️ Пройти тест и забрать скидку: https://vk.cc/cLkRLn Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

💡Сегодня покажу крутую фишку для оптимизации чтения больших коллекций из базы через JPA. 📌 Проблема: Когда загружаем большу
💡Сегодня покажу крутую фишку для оптимизации чтения больших коллекций из базы через 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 может сломать приложение под нагрузкой. Оптимизируйте загрузку коллекций заранее! 👉@BookJava

1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟 Как меняется ИТ-индустрия с внедрением AI? Узнай 6 июня на ИТ-конференции МТС True Tech Day Tru
1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟 Как меняется ИТ-индустрия с внедрением AI? Узнай 6 июня на ИТ-конференции МТС True Tech Day True Tech Day 2025 — третья масштабная технологическая конференция МТС для профессионалов ИТ‑индустрии. В программе: — Больше 40 докладов от известных ученых и ИТ-компаний. — Выступления зарубежных спикеров с индексом Хирша более 50. — Концентрация практических кейсов: как создаются большие проекты с применением AI. — Доклады по архитектуре, бэкенд-разработке и построению ИТ-платформ. — AI-интерактивы и технологические квесты. — Пространство для нетворкинга, …а еще after-party со звездным лайн-апом. Когда: 6 июня Где: Москва, МТС Live Холл и онлайн Участие бесплатно.Регистрация по ссылке.

Как работают 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(). 👉@BookJava

В 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 полезны для общей инициализации, которую нужно выполнять вне зависимости от того, какой конструктор вызывается. 👉@BookJava

Есть фича. Есть дедлайн. Есть понимание, что тесты надо писать… но они отъедают время, которого итак впритык. Один наш знаком
Есть фича. Есть дедлайн. Есть понимание, что тесты надо писать… но они отъедают время, которого итак впритык. Один наш знакомый девелопер сказал: «С тех пор как поставил Explyt Test — начал писать меньше тестов… но покрытие стало лучше. Как это вообще возможно?!» Попробуйте сами. Плагин сам предлагает тесты для вашего кода — прямо в IDE. 👉 explyt.ai — сэкономь себе пару часов уже сегодня.

🧠 Сегодня покажу вам полезный трюк для Spring Boot: как элегантно валидировать параметры контроллера без лишнего кода. Часто
🧠 Сегодня покажу вам полезный трюк для 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 аннотации на контроллере валидация параметров работать не будет! 👉@BookJava

📌 Ошибка: Вызов метода с @Transactional внутри того же класса не запускает новую транзакцию. Почему? Spring оборачивает бин
📌 Ошибка: Вызов метода с @Transactional внутри того же класса не запускает новую транзакцию. Почему? Spring оборачивает бин в прокси, а внутренние вызовы проходят мимо прокси, значит, аннотация игнорируется. Пример проблемы:

@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        saveOrder();
    }

    @Transactional
    public void saveOrder() {
        // Новый транзакционный контекст не создастся!
    }
}
💡 Как правильно: 1. Вынести saveOrder() в отдельный бин. 2. Или получить прокси текущего бина через AopContext:

@Service
@EnableAspectJAutoProxy(exposeProxy = true) // важно!
public class OrderService {

    @Transactional
    public void createOrder() {
        ((OrderService) AopContext.currentProxy()).saveOrder();
    }

    @Transactional
    public void saveOrder() {
        // Теперь всё ок ✅
    }
}
⚠️ Важный момент: exposeProxy = true нужен на уровне конфигурации, иначе AopContext не заработает. Понимание этой тонкости критично для корректного управления транзакциями в Spring! 🚀 👉@BookJava

🚀 Подборка Telegram каналов для программистов Системное администрирование, DevOps 📌 https://t.me/bash_srv Bash Советы https://t.me/win_sysadmin Системный Администратор Windows https://t.me/sysadmin_girl Девочка Сисадмин https://t.me/srv_admin_linux Админские угодья https://t.me/linux_srv Типичный Сисадмин https://t.me/devopslib Библиотека девопса | DevOps, SRE, Sysadmin https://t.me/linux_odmin Linux: Системный администратор https://t.me/devops_star DevOps Star (Звезда Девопса) https://t.me/i_linux Системный администратор https://t.me/linuxchmod Linux https://t.me/sys_adminos Системный Администратор https://t.me/tipsysdmin Типичный Сисадмин (фото железа, было/стало) https://t.me/sysadminof Книги для админов, полезные материалы https://t.me/i_odmin Все для системного администратора https://t.me/i_odmin_book Библиотека Системного Администратора https://t.me/i_odmin_chat Чат системных администраторов https://t.me/i_DevOps DevOps: Пишем о Docker, Kubernetes и др. https://t.me/sysadminoff Новости Линукс Linux 1C разработка 📌 https://t.me/odin1C_rus Cтатьи, курсы, советы, шаблоны кода 1С https://t.me/DevLab1C 1С:Предприятие 8 https://t.me/razrab_1C 1C Разработчик https://t.me/buh1C_prog 1C Программист | Бухгалтерия и Учёт https://t.me/rabota1C_rus Вакансии для программистов 1С Программирование C++📌 https://t.me/cpp_lib Библиотека C/C++ разработчика https://t.me/cpp_knigi Книги для программистов C/C++ https://t.me/cpp_geek Учим C/C++ на примерах Программирование Python 📌 https://t.me/pythonofff Python академия. https://t.me/BookPython Библиотека Python разработчика https://t.me/python_real Python подборки на русском и английском https://t.me/python_360 Книги по Python Java разработка 📌 https://t.me/BookJava Библиотека Java разработчика https://t.me/java_360 Книги по Java Rus https://t.me/java_geek Учим Java на примерах GitHub Сообщество 📌 https://t.me/Githublib Интересное из GitHub Базы данных (Data Base) 📌 https://t.me/database_info Все про базы данных Мобильная разработка: iOS, Android 📌 https://t.me/developer_mobila Мобильная разработка https://t.me/kotlin_lib Подборки полезного материала по Kotlin Фронтенд разработка 📌 https://t.me/frontend_1 Подборки для frontend разработчиков https://t.me/frontend_sovet Frontend советы, примеры и практика! https://t.me/React_lib Подборки по React js и все что с ним связано Разработка игр 📌 https://t.me/game_devv Все о разработке игр Библиотеки 📌 https://t.me/book_for_dev Книги для программистов Rus https://t.me/programmist_of Книги по программированию https://t.me/proglb Библиотека программиста https://t.me/bfbook Книги для программистов БигДата, машинное обучение 📌 https://t.me/bigdata_1 Big Data, Machine Learning Программирование 📌 https://t.me/bookflow Лекции, видеоуроки, доклады с IT конференций https://t.me/rust_lib Полезный контент по программированию на Rust https://t.me/golang_lib Библиотека Go (Golang) разработчика https://t.me/itmozg Программисты, дизайнеры, новости из мира IT https://t.me/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 https://t.me/nodejs_lib Подборки по Node js и все что с ним связано https://t.me/ruby_lib Библиотека Ruby программиста https://t.me/lifeproger Жизнь программиста. Авторский канал. QA, тестирование 📌 https://t.me/testlab_qa Библиотека тестировщика Шутки программистов 📌 https://t.me/itumor Шутки программистов Защита, взлом, безопасность 📌 https://t.me/thehaking Канал о кибербезопасности https://t.me/xakep_2 Хакер Free Книги, статьи для дизайнеров 📌 https://t.me/ux_web Статьи, книги для дизайнеров Математика 📌 https://t.me/Pomatematike Канал по математике https://t.me/phis_mat Обучающие видео, книги по Физике и Математике https://t.me/matgeoru Математика | Геометрия | Логика Excel лайфхак📌 https://t.me/Excel_lifehack https://t.me/mir_teh Мир технологий (Technology World) Вакансии 📌 https://t.me/sysadmin_rabota Системный Администратор https://t.me/progjob Вакансии в IT

Сегодня покажу вам 🧠 один тонкий момент в работе с Optional, который часто ловит даже опытных. Многие пишут так: Optional op
Сегодня покажу вам 🧠 один тонкий момент в работе с Optional, который часто ловит даже опытных. Многие пишут так:

Optional<String> optional = getValue();
if (optional.isPresent()) {
    doSomething(optional.get());
}
⚠️ Это антипаттерн! Вы теряете суть Optional и рискуете ошибками в многопоточке. 📌 Правильный способ — использовать функциональный стиль:

getValue().ifPresent(this::doSomething);
Или ещё элегантнее:

getValue()
    .map(this::transform)
    .filter(this::isValid)
    .ifPresent(this::doSomething);
💡 Подходы: - map для преобразования значения - filter для отсеивания ненужных - orElse, orElseGet, orElseThrow для обработки отсутствия - ifPresentOrElse в Java 9+ для двух вариантов действий 📈 Выгоды: - Код становится компактнее - Безопаснее при рефакторинге - Лучше для чтения в потоковых операциях (Stream API) 🛠 Если нужно вынуть значение — используйте orElseThrow() вместо .get():

String value = getValue().orElseThrow(() -> new IllegalStateException("Value not found"));
👉 Отказывайтесь от .isPresent() и .get() связки — используйте силу функционального подхода! 🚀 👉@BookJava

🤖 Хотите автоматизировать инфраструктуру для тестирования прямо в Gradle? На открытом уроке «Облако в кармане: запускаем всю
🤖 Хотите автоматизировать инфраструктуру для тестирования прямо в Gradle? На открытом уроке «Облако в кармане: запускаем всю инфраструктуру для теста при сборке» от OTUS мы расскажем, как избежать ручных настроек и запусков. Вместо этого вы научитесь автоматизировать весь процесс с помощью Docker, DockerCompose и TestContainers, интегрируя их с Gradle. Урок полезен для разработчиков на Kotlin и Java, которые работают с автотестами — интеграционными и end-to-end. В ходе урока вы освоите: ▫️Автоматический запуск всей необходимой инфраструктуры для тестирования. ▫️Создание Docker-образов для тестов и деплоя. ▫️Разработку автотестов, которые поднимут ваши навыки на новый уровень. Участники получат скидку на курс «Kotlin Backend Developer. Professional». ➡️ Встречаемся 29 апреля в 20:00 МСК, регистрация открыта: https://vk.cc/cL78v6 Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

🧠 JPA: подводный камень с @ElementCollection и fetch = FetchType.EAGER Сегодня покажу, почему @ElementCollection(fetch = Fet
🧠 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 — он не так прост, как кажется. 👉@BookJava

🧠 Как избежать N+1 при использовании @OneToMany в JPA Одна из самых коварных ловушек JPA - это N+1 проблема. Особенно часто
🧠 Как избежать 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 или подзапросы. 👉@BookJava

👩‍💻 Разработка на Java требует глубокого понимания не только языка, но и принципов работы JVM, многопоточности и современны
👩‍💻 Разработка на Java требует глубокого понимания не только языка, но и принципов работы JVM, многопоточности и современных фреймворков. Курс «Java Developer. Professional» — это структурированное обучение для разработчиков, которые хотят выйти на новый уровень, освоить актуальный стек технологий и уверенно претендовать на позиции уровня Middle+. 🦾 Вы получите 96 часов практической работы, обучение на живых вебинарах, вы разберете ключевые аспекты работы JVM, научитесь строить эффективные многопоточные приложения, освоите Spring WebFlux, Kafka, реактивный Postgres и Kubernetes. Программа OTUS постоянно обновляется, соответствуя требованиям рынка, а диплом ценится работодателями. 👉 Пройдите вступительное тестирование и присоединяйтесь к группе: https://vk.cc/cL3cnt 🎁 Начните обучение со скидкой, подробности у менеджеров. ПРОМОКОД: JAVA_04 Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576