Hibernate / JPA: вопросы с ответами
36 разобранных вопросов по теме «Hibernate / JPA». Каждый — с правильным ответом и пояснением.
- В чём разница: Content-Type: json vs form-urlencoded vs multipart?
application/json: JSON в body (REST API). application/x-www-form-urlencoded: пары key=value (формы логина). multipart/form-data: для загрузки файлов (binary + metadata).
- Entity как ключ HashMap — что может пойти не так?
Если equals/hashCode используют lazy-связи (ManyToOne, OneToMany) — при первом вызове hashCode загрузится N связанных сущностей (N+1). Хуже: если связи двусторонние — бесконечная рекурсия и StackOverflowError.
- Hibernate vs JDBC — в чём разница?
JDBC — низкий уровень, прямой SQL, ResultSet, ручной маппинг колонок в поля. Hibernate — поверх JDBC: сам генерирует обычный SQL и маппит результаты в объекты. Удобно для типового CRUD.
- JdbcTemplate — когда уместен вместо Hibernate?
Сложные аналитические запросы (оконные функции, CTE). Batch-операции (тысячи INSERT). Запросы, где ORM генерирует неоптимальный SQL. Миграция legacy-кода. Простые CRUD без сложных связей — везде, где нужен полный контроль над SQL.
- В чём разница: JOOQ vs Hibernate?
JOOQ: типизированный SQL, compile-time проверка запросов, нет dirty checking. Hibernate: ORM, lazy loading, кэширование. В Путешествиях используют оба — JOOQ для сложных запросов, Hibernate для CRUD.
- В чём разница: JPA vs Hibernate?
JPA — спецификация (набор интерфейсов и аннотаций). Hibernate — одна из её реализаций (наряду с EclipseLink, OpenJPA). EntityManager — JPA-интерфейс, Session — нативный Hibernate-интерфейс, расширяющий спецификацию.
- В чём разница: Lazy vs Eager загрузка?
Lazy — связь подгружается только при обращении (прокси). Eager — сразу в SELECT. Default для @OneToMany и @ManyToMany — Lazy, для @ManyToOne и @OneToOne — Eager.
- N+1 в JPA — как диагностировать?
Включить логи SQL (spring.jpa.show-sql=true), смотреть количество запросов на эндпоинт, использовать p6spy или Hibernate Statistics.
- N+1 — детально?
1 findAll() + N доп. запросов на lazy-коллекции при обращении к ним: 1000 заказов → 1001 SQL. Обнаружение: hibernate.generate_statistics=true, datasource-proxy. Решения: JOIN FETCH, @EntityGraph, @BatchSize(100), @Fetch(FetchMode.SUBSELECT).
- Что такое N+1 проблема?
1 findAll() + N доп. запросов на lazy-коллекции. Решения: JOIN FETCH, @EntityGraph, @BatchSize(100), DTO-проекция. EAGER — неправильный ответ.
- Что такое N+1 проблема в Spring Data JPA?
При fetch = LAZY на связи, при итерации по списку делается +1 запрос за каждую связанную сущность. Решения: JOIN FETCH, @EntityGraph, BatchSize.
- В чём разница: Pessimistic vs optimistic locking?
Pessimistic: SELECT ... FOR UPDATE блокирует строку. Optimistic: версия (@Version) — при commit проверяется, изменилась ли она.
- В чём разница: @SpringBootTest vs @WebMvcTest vs @DataJpaTest?
@SpringBootTest: поднимает ВЕСЬ контекст (медленно, для интеграционных/E2E). @WebMvcTest: только веб-слой (контроллеры + MockMvc, без БД). @DataJpaTest: только JPA-слой + встроенная БД, с откатом транзакций после каждого теста.
- В чём разница DTO и Entity?
Entity — связана с БД (@Entity). Может иметь связи (LazyInitializationException наружу!). DTO — независимый объект для передачи. Хорошая практика: НЕ возвращать Entity из REST-контроллеров, маппить в DTO, чтобы не тянуть ленивые связи вне сессии.
- В чём разница JPA и Hibernate?
JPA (Java Persistence API) — спецификация (интерфейсы и аннотации в пакете javax.persistence / jakarta.persistence). Hibernate — её конкретная реализация (как EclipseLink и OpenJPA), которая вдобавок предоставляет собственные расширения: HQL, кэш второго уровня и т.д.
- Как решить N+1?
(1) JPQL с JOIN FETCH: select o from Order o join fetch o.user. (2) @EntityGraph — атрибут на репозиторий-методе, говорит «подгрузить эти поля сразу». (3) Hibernate default_batch_fetch_size — группирует SELECT'ы по пачкам вместо запроса на каждую сущность.
- Как сделать пагинацию?
Метод принимает Pageable, возвращает Page или Slice. Spring Data сам добавит LIMIT/OFFSET.
- Какие виды связей бывают?
@OneToOne — один к одному (User ↔ Passport). @OneToMany / @ManyToOne — один ко многим / многие к одному (User → Orders). @ManyToMany — многие ко многим (Student ↔ Course, через промежуточную таблицу).
- Какой Fetch type по умолчанию?
Для коллекций (@OneToMany, @ManyToMany) — Lazy. Для скалярных связей (@OneToOne, @ManyToOne) — Eager. Eager по умолчанию для @ManyToOne — частая ловушка: при загрузке Order сразу подтянется User, что провоцирует проблему N+1.
- Каскадные операции: что делает CascadeType.ALL?
PERSIST + MERGE + REMOVE + REFRESH + DETACH. Опасность: CascadeType.REMOVE при OneToMany — удаление родителя удалит ВСЕХ детей. Если детей 10 000 — 10 000 DELETE запросов.
- Что такое Кэши Hibernate?
First-level: автоматический, привязан к Session. Гарантирует identity (один PK = один объект). Second-level: опциональный, общий на уровне SessionFactory (EhCache, Caffeine, Infinispan). Query cache: кэширует результаты JPQL-запросов и требует включённого second-level.
- Что такое Оптимистичная блокировка?
@Version (int/long/Timestamp). UPDATE ... WHERE version = ?. Если не совпала — OptimisticLockException. Подходит для low-contention (веб-приложения). Пессимистичная: @Lock(PESSIMISTIC_WRITE) → SELECT FOR UPDATE — блокирует строку.
- Что такое Оптимистичная блокировка в JPA?
Поле @Version (int/long/timestamp). При update Hibernate сравнивает версию — если не совпала, бросает OptimisticLockException.
- Что такое Проблема N+1?
При итерации по списку на каждую связанную сущность делается отдельный SELECT. Решения: JOIN FETCH, @EntityGraph, @BatchSize, @Fetch(FetchMode.SUBSELECT).
- Разница между get() и load() в Hibernate?
get() — сразу идёт в БД и возвращает null если не найдено. load() — возвращает прокси, БД дёргается только при обращении к полям; если нет записи — ObjectNotFoundException.
- Разница между save(), persist(), merge(), update()?
persist — для новых (JPA стандарт, не возвращает id). save — Hibernate-specific, возвращает id. merge — копирует detached в managed. update — прикрепляет detached.
- Что такое Состояния сущности?
Transient — new User(), Hibernate не знает. Persistent/Managed — связан с сессией, dirty checking автоматически синхронизирует изменения. Detached — сессия закрыта/detach, изменения сами в БД не уходят, нужен merge(). Removed — помечен на удаление. Всего четыре состояния.
- Что такое Состояния сущности в Hibernate?
Transient (новый объект, Hibernate не знает), Persistent/Managed (связан с сессией), Detached (был persistent, но сессия закрыта), Removed (помечен на удаление).
- Чем отличается JpaRepository от CrudRepository?
JpaRepository extends PagingAndSortingRepository extends CrudRepository. Добавляет flush(), saveAllAndFlush(), работу с List вместо Iterable, deleteAllInBatch.
- Что лучше — Lazy или Eager?
Lazy. Eager почти всегда плохо — Hibernate тянет лишнее, и часто это даёт N+1 в неожиданном месте. Правило: ставь Lazy везде, а конкретный JOIN FETCH делай в запросах, где данные действительно нужны.
- Что такое DTO?
Data Transfer Object — объект для передачи данных между слоями системы или между сервисами. Не содержит логики, только поля и геттеры/сеттеры. Часто используется как тело REST-запросов и ответов.
- Что такое N+1?
Загрузил список из N сущностей одним запросом. Потом в цикле обращаешься к их LAZY-связям — на каждую идёт отдельный SELECT. Итого 1 + N запросов. На 100 сущностях это 101 запрос — катастрофа для производительности.
- Что такое ORM?
Object-Relational Mapping — отображение объектов на таблицы БД: класс User → таблица users, поля → колонки, связи (@OneToMany) → JOIN'ы. Hibernate сам генерирует SQL, а разработчик работает с объектами.
- Что такое POJO?
Plain Old Java Object — обычный Java-класс без специальных требований (наследования от чего-то, реализации интерфейсов фреймворка). Просто поля + геттеры + сеттеры + конструктор.
- Что такое Spring Data JPA?
Удобная обёртка над JPA / Hibernate: создаёшь интерфейс-наследник JpaRepository — Spring сам генерирует реализацию. Query methods: по имени метода вроде findByEmail Spring сам собирает SQL-запрос, работая с @Entity-классами.
- Что такое VO?
Value Object — объект, который определяется ТОЛЬКО своими значениями (не identity). Например, Money(amount, currency) — два VO с одинаковыми полями равны. Иммутабельны, имеют equals/hashCode по содержимому.