Как работает Spring Data JPA и для чего нужна эта прослойка
Что проверяют
Различаете ли вы три слоя: JPA как спецификацию, Hibernate как реализацию и Spring Data JPA как надстройку над ними. Следом просят объяснить, откуда берётся реализация у интерфейса без единой строки кода, и спрашивают про N+1.
Как отвечать
Jakarta Persistence (пакет jakarta.persistence со Spring Boot 3) описывает EntityManager, сущности и JPQL. Hibernate это реализация, которая превращает операции над сущностями в SQL и ведёт контекст персистентности. Spring Data JPA сам в базу ничего не шлёт: он убирает шаблонный код DAO поверх EntityManager.
Как это устроено:
- репозиторий: вы объявляете
interface UserRepository extends JpaRepository<User, Long>, а при старте Spring создаёт прокси, за которым стоитSimpleJpaRepositoryс готовымиsave,findById,findAll; - производные запросы: имя
findByEmailAndStatusразбирается при старте, опечатка в имени поля роняет запуск, а не запрос; @Query: свой JPQL или SQL сnativeQuery = true, для изменений нужен@Modifying;- выборки:
Pageable,PageиSlice, проекции на интерфейсы или записи,Specificationдля динамических фильтров,@EntityGraphдля подгрузки связей одним запросом; - транзакции: методы
SimpleJpaRepositoryуже помечены@Transactional, чтение сreadOnly = true.
Зачем прослойка: меньше однотипного кода, пагинация и аудит (@CreatedDate) из коробки. Цена: SQL скрыт, и его надо уметь увидеть.
Хорошая деталь: save() вызывает persist для новой сущности и merge для существующей. Новизна определяется по null в id или версии, поэтому при id, заданном вручную, каждое сохранение начинается с лишнего SELECT.
Где спотыкаются
Говорят, что Spring Data JPA заменяет Hibernate. Без реализации JPA он не работает, и все проблемы ленивой загрузки остаются.
Не видят N+1: в цикле по списку заказов обращаются к order.getItems(), и на каждый заказ уходит отдельный запрос. Лечится JOIN FETCH или @EntityGraph.
Пишут findAll() на большой таблице или производный deleteByStatus, который сначала загружает все сущности и удаляет их по одной. Для массовых операций нужен @Modifying с @Query.