Как принимается решение использовать Hibernate или писать SQL-запросы напрямую
Что проверяют
Есть ли у вас критерии выбора, а не вкусовое «ORM медленный» или «SQL это прошлый век». Следом просят пример из практики, где Hibernate пришлось заменить на запрос, и спрашивают про N+1.
Как отвечать
Сформулируйте критерий через характер работы с данными:
- Hibernate (JPA): богатая доменная модель и типичный CRUD над агрегатами. Даёт dirty checking (изменили поле внутри транзакции,
UPDATEуйдёт сам), кеш первого уровня, каскады, оптимистическую блокировку через@Version, переносимость между СУБД; - SQL напрямую: отчёты, аналитика, оконные функции, CTE, массовые обновления, возможности конкретной СУБД вроде
ON CONFLICTили JSONB. Инструменты:JdbcTemplateилиJdbcClientв Spring, jOOQ для типобезопасного SQL, нативный запрос через@Query(nativeQuery = true).
На практике подходы смешивают в одном проекте: запись и бизнес-логика через сущности, чтение для экранов и отчётов через DTO-проекции или SQL.
Покажите, что знаете, где Hibernate обходится дорого и как это лечится:
- N+1: лечится
JOIN FETCH,@EntityGraphили@BatchSize; @ManyToOneпо умолчаниюEAGER: связь стоит явно переводить вLAZY;- пакетная вставка: нужна настройка
hibernate.jdbc.batch_size, а генерация id черезIDENTITYпакетную вставку отключает, поэтому для массовой записи берутSEQUENCE; - массовые операции: загружать тысячи сущностей ради одного поля дороже, чем выполнить один
UPDATE.
Проверять решение стоит по логам SQL (spring.jpa.show-sql или логгер org.hibernate.SQL) и по плану запроса, а не на глаз.
Где спотыкаются
Утверждают, что с чистым SQL транзакции придётся писать вручную. @Transactional в Spring одинаково работает и с JPA, и с JdbcTemplate.
Смешивают нативный UPDATE с сущностями в одной транзакции. Контекст персистентности об изменении не знает и отдаёт старые данные, нужен clear() или @Modifying(clearAutomatically = true).
Выбирают подход один раз на весь проект. Сильнее звучит ответ «ORM по умолчанию, SQL там, где план запроса или объём данных этого требуют».