Открытый канал ID 77400161222906

Java. Менторинг за чашкой кода

Канал о Java и про Java, и, конечно, для тех кто на ней кодит, ну или только собирается. Подробно разбираю вопросы к собеседованию на Java различного уровня

Каналов в рубрике
1 729
Место этого канала
№ 225
Открыть рубрику →

Публикации за период

По реакциям

Поиск и вся лента — после бесплатной регистрации. Свои даты — по подписке; владелец канала видит их бесплатно. Сортировка по реакциям — по подписке. Зарегистрироваться

Загрузка…

Продолжаем разбор Hibernate. В первой части мы выучили словарь, теперь переходим к самому интересному

Hibernate: Продвинутый уровень

1. Состояния Entity (Жизненный цикл)

Объект в Hibernate — это не просто данные, это объект с состоянием. Их три:
* Transient (Временный): Объект создан через `new User()`, но Session о нем не знает. В БД его нет.
* Persistent (Постоянный): Объект прикреплен к Session (`session.save(user)` или `session.get(User.class, id)`). Любое изменение этого объекта отслеживается (Dirty Checking) и при коммите улетит в БД.
* Detached (Отсоединенный): Объект был Persistent, но Session закрылась. Он жив в памяти, но Hibernate за ним больше не следит. Чтобы вернуть его в игру, используют `merge()`.

2. Методы get() vs load() (Классика жанра)
Разница в том, как они ходят в базу:
* `get()`: Идет в БД **сразу**. Если объекта нет — возвращает `null`.
* `load()`: Возвращает **Прокси-объект** (заглушку) сразу. В БД идет только тогда, когда вы попытаетесь обратиться к полям объекта (Lazy Initialization). Если объекта нет — при обращении к полям кинет `ObjectNotFoundException`.
* Совет: Используйте `get()`, если не уверены, что объект есть. Используйте `load()`, если уверены (например, для установки связи `order.setUser(user)`).

3. save() vs persist() vs saveOrUpdate()
Это главная боль Junior-разработчиков.
* `persist()`: Делает объект Persistent. Работает только внутри транзакции. Не делает `INSERT` сразу (может сделать позже при flush). Гарантирует соблюдение спецификации JPA.
* `save()`: Старое Hibernate-API. Возвращает `Serializable` (ID объекта). Может сделать `INSERT` сразу (вне транзакции), что ломает логику JPA.
* `saveOrUpdate()`: Устаревший метод. Если ID есть — делает `UPDATE`, если нет — `INSERT`. Сейчас лучше использовать `merge()`.

4. Кэширование (Уровни кэша)
* L1 Cache (Session Cache): Есть у каждой Session по умолчанию. Если в рамках одной сессии вы дважды попросили юзера с `id=1`, Hibernate пойдет в БД только один раз. Очищается при закрытии Session.
* L2 Cache (SessionFactory Cache): Общий для всех сессий. Нужен, если данные читаются часто, а меняются редко (например, справочники: города, страны). Настраивается через провайдеры (EhCache, Redis).

5. Lazy Loading и Проблема N+1
* Lazy: Связанные коллекции (`@OneToMany`) не грузятся из БД, пока вы к ним не обратитесь. Это экономит память.
* Проблема N+1: У вас есть 100 заказов. В цикле вы делаете `order.getItems()()`. Hibernate делает 1 запрос, чтобы найти 100 заказов, и еще 100 запросов, чтобы для каждого заказа найти его товары. Итого 101 запрос.
* Решение: `JOIN FETCH` в HQL (`FROM Order o JOIN FETCH o.items`) или `@EntityGraph`.

6. Flush и Commit
* `flush()`: Выгружает изменения из Session в БД (генерирует SQL), но **не завершает транзакцию**. Если после `flush()` случится ошибка, можно откатить.
* `commit()`: Завершает транзакцию. Обычно внутри себя делает `flush()`.
* Зачем вручную? Если вам нужно получить ID объекта *до* коммита (например, чтобы записать его в лог или передать в другой сервис).

Итог:
Hibernate кажется магией, пока вы не поймете, что это просто умный SQL-генератор, который держит граф объектов в памяти. Как только вы поймете, как работает Session и Proxy, 90% багов исчезнут.
Просмотры: 67
Перейти в пост Открыть в MAX Сообщить о неточности
🛠️ Словарь Java-разработчика: 10 терминов Hibernate, которые нужно знать

Hibernate — это не просто библиотека, это целый мир со своим языком. Если вы начинаете новый проект со Spring Boot, без этих определений вы «поплывете» на первом же вопросе техлида.

1. ORM (Object-Relational Mapping)
Это сама концепция. Мост между миром Java (объекты, классы, наследование) и миром БД (таблицы, строки, связи). Hibernate берет ваш класс `User` и сам превращает его в строку в таблице `users`.

2. Entity (Сущность)
Обычный Java-класс (POJO), помеченный аннотацией `@Entity`. Это «паспорт» объекта. Hibernate говорит: «Ага, этот класс — не просто код, это данные, которые я должен сохранить в БД».

3. Session (org.hibernate.Session)
Главный рабочий инструмент. Именно через `Session` вы просите: «Сохрани это», «Найди то», «Удали вон то». Важно: `Session` — это не поток (Thread), это обычно короткоживущий объект внутри транзакции.

4. SessionFactory (org.hibernate.SessionFactory)
Тяжелая, неповоротливая, но очень важная штука. Она производит `Session` и создается один раз при старте приложения. Она читает конфиги, маппинги и строит в памяти карту вашей базы данных.

5. JPA (Java Persistence API)
Это спецификация (набор правил и аннотаций), написанная Oracle. Hibernate — это реализация этой спецификации.
Аналогия: JPA — это USB-порт (стандарт), а Hibernate — это флешка, которую вы в него втыкаете. Вы можете заменить Hibernate на EclipseLink, и ваш код почти не изменится, потому что оба работают по правилам JPA.

6. HQL (Hibernate Query Language)
Язык запросов, похожий на SQL, но пишется для объектов, а не для таблиц.
*SQL:* `SELECT * FROM users WHERE name = 'Ivan'`
*HQL:* `FROM User u WHERE u.name = 'Ivan'`
Вы просите «Дай мне объекты User», а не «Дай мне строки из таблицы».

7. Lazy Loading (Ленивая загрузка)
Спасение от `OutOfMemoryError`. По умолчанию Hibernate не грузит связанные данные (например, список `Order` для `User`), пока вы явно их не попросите.
Проблема (N+1): Если в цикле попросить заказы для 100 пользователей, Hibernate сделает 101 запрос к БД. Лечится `JOIN FETCH`.

8. Dirty Checking (Проверка на грязь)
Магия Hibernate. Перед закрытием транзакции он сравнивает состояние объектов в памяти с тем, что было считано из БД. Если поле `name` изменилось, он сам сгенерирует `UPDATE`. Вам не нужно писать `save()`, если вы просто поменяли геттер/сеттер внутри транзакции.

9. Cascade (Каскадирование)
Правило «семейственности». Если вы удаляете `User`, что делать с его `Order`?
`CascadeType.ALL` скажет: «Удаляй всё вместе с хозяином». `CascadeType.PERSIST` скажет: «Если сохраняешь юзера, сохрани и его новый адрес автоматически».

10. Flush (Сброс)
Это момент, когда Hibernate берет накопленные в памяти изменения (`save`, `update`) и отправляет их в БД в виде SQL-запросов. Обычно происходит при коммите транзакции. Но `session.flush()` можно вызвать принудительно, если нужно, чтобы ID сгенерировался прямо сейчас (например, чтобы записать его в лог).

Итог: Hibernate берет на себя рутину (JDBC, ResultSet, Connection pooling), но взамен требует понимания того, как работает сессия и ленивая загрузка.
Просмотры: 77
Перейти в пост Открыть в MAX Сообщить о неточности
Всем привет, сегодня заглянем под капот JVM, где на самом деле живёт int в JVM?
Все пишут int a = 10;
но что под капотом делает JVM?
int — это не объект, а примитив.

Он не создаётся через new, не лежит в куче по умолчанию и не проходит через конструктор. Для него у JVM есть отдельные байт-код инструкции: iload — положить на стек, istore — сохранить обратно, iadd — сложить напрямую. Никаких вызовов методов, никакого заголовка объекта, никакого участия GC.

Где хранится int — зависит от того, чем является переменная:
- Локальная переменная
→ лежит в массиве locals текущего стекового фрейма. Компилятор превращает имя переменной в «номер слота», а рантайм просто читает и пишет по индексу. Метод завершился — фрейм вытолкнулся, и этот int исчез. Сборщику мусора тут делать нечего.

- Поле экземпляра → лежит внутри объекта в куче. У объекта фиксированный layout, а у поля есть смещение (offset). Есть ссылка на объект — прибавили смещение — нашли этот int.
- Статическое поле → живёт вместе с объектом Class, который сам по сути лежит в куче. Время жизни привязано к загрузчику классов.

Почему int такой «низкоуровневый»? Потому что Java с самого начала разделила типы на примитивные и ссылочные. int — это 32-битное знаковое число в дополнительном коде, диапазон от -2³¹ до 2³¹−1. JVM работает с ним как с числом в регистре, без всякой «распаковки».
И только когда int нужно использовать как объект (например, положить в List<Integer>), компилятор автоматически упаковывает его в Integer: вот тогда появляется объект, заголовок, куча и GC.

Коротко: int в JVM — это по сути «отрезок из 32 бит». Где он лежит — зависит от того, где ты его объявил. На стеке — живёт мгновение, в куче — вместе с объектом, статический — вместе с классом.
Просмотры: 58
Перейти в пост Открыть в MAX Сообщить о неточности
Всем привет,
Сегодня поговорим о таком недооцененно инструменте, как Row-Level Security (RLS) — политика безопасности на уровне строк. Это фича PostgreSQL (и других СУБД), которая работает как невидимый `WHERE`, автоматически добавляемый к каждому вашему запросу.

Как это работает?
Представьте таблицу `documents`. Там лежат документы и для бухгалтерии, и для юристов, и для гендиректора.
Обычно вы пишете: `SELECT * FROM documents WHERE department_id = 5`.

С RLS вы просто пишете: `SELECT * FROM documents`.
А база сама дописывает: `...AND current_user = documents.author`.

Пользователь `ivan` даже не увидит строки, созданные пользователем `petr`. Для него таблица `documents` будет содержать только его собственные файлы.

Где это спасает?

1. SaaS-сервисы (Борьба с «проблемой соседа»).
Самый частый баг в стартапах — когда один клиент видит данные другого из-за ошибки в `JOIN` или забытого `WHERE` в сложном отчете.
С RLS это невозможно физически. Вы ставите политику: `USING (tenant_id = current_setting('app.current_tenant')::int)`. Теперь, даже если разработчик напишет `SELECT * FROM orders` без фильтра по клиенту, база отдаст только заказы текущей компании.

2. Мягкое удаление (Soft Delete).
Вместо того чтобы писать `WHERE deleted_at IS NULL` в каждом запросе (и обязательно забыть его в одном из 100 мест), вы делаете это политикой RLS. Для всех «обычных» запросов удаленные строки просто перестают существовать. Они есть в таблице, но база их скрывает.

3. Аудит и защита от «слива».
Вы можете запретить `SELECT *` даже администратору БД (если он залогинен под обычным пользователем), ограничив видимость только определенными колонками или строками.

Подводный камень:
RLS — это не бесплатно. Это дополнительная нагрузка на планировщик. Если политик много и они сложные, время планирования запроса (`Planning Time`) может вырасти. И главное — если вы забудете включить `RLS ENABLE` на таблице, политика работать не будет.

RLS — это, если бэкенд ошибся, база данных не даст украсть данные. Он делает вашу систему по-настоящему защищенной
Просмотры: 79
Перейти в пост Открыть в MAX Сообщить о неточности
Всем привет, сегодня поговорим про индексы в PostgreSQL.

Мы привыкли думать, что индекс — это просто «скоростная шпаргалка» для поиска. Но у него есть темная сторона, о которой молчат.

Факт: Индекс живет своей отдельной жизнью
Когда вы обновляете всего одну строку в таблице, база данных не меняет её «по месту».
1. Она создает новую версию строки (это называется MVCC).
2. А в каждом индексе, который есть на этой таблице, она тоже создает новую запись.

Если у вас на таблице 10 индексов, то при изменении одной строки база делает 11 физических записей на диск.

Почему это важно?
Это главная причина, почему индексы нужно удалять.
Каждый «лишний» индекс не просто занимает место на SSD. Он превращает легкие операции `UPDATE` и `INSERT` в тяжелую работу. База вынуждена обновлять дерево индекса, пересортировывать его и записывать на диск снова и снова.

Например:
У вас есть колонка `last_login_date`. Вы решили сделать по ней индекс, «на всякий случай».
Теперь каждый раз, когда пользователь просто заходит на сайт, Postgres:
* Записывает новую строку в таблицу.
* Записывает новую запись в индекс `last_login_date`.
* Записывает новые записи во все остальные 5 индексов таблицы.

Если пользователей много, этот «лишний» индекс может стать бутылочным горлышком и замедлить работу всего приложения.

Как проверить?
Зайдите в `pg_stat_user_indexes`. Если вы видите индекс, у которого `idx_scan` (счетчик использования) равен 0, а `idx_tup_upd` (счетчик обновлений) растет — этот индекс вреден. Он ничего не ускоряет, но заставляет базу работать на износ при каждой записи.

Индекс — это не бесплатно. Это компромисс.
Правило: Если вы ищете по полю редко, а пишете в него часто — индекс, скорее всего, не нужен.
Просмотры: 74
Перейти в пост Открыть в MAX Сообщить о неточности
ВКонтакте Телеграм Одноклассники
Избранное в datool.ru

Следите за каналом в личном кабинете

Зарегистрируйтесь в datool.ru, чтобы добавить канал «Java. Менторинг за чашкой кода» в избранное и отслеживать его показатели.

Зарегистрироваться
Для владельцев каналов

Подтвердите свой канал

Войдите или зарегистрируйтесь в datool.ru, затем добавьте нашего бота в канал по инструкции в личном кабинете. После проверки владения замок исчезнет и вам станет доступно управление каналом.

Войти или зарегистрироваться

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти