Что такое нативный SQL-запрос в Hibernate и когда его используют?

Ответ

Нативный запрос в Hibernate — это обычный SQL-запрос, который выполняется напрямую в базе данных, минуя HQL/JPQL и часть механизмов ORM (например, автоматическую выборку по связям).

Как создать нативный запрос:

// Создание запроса с явным указанием класса сущности для маппинга результата
List<User> users = session.createNativeQuery(
        "SELECT * FROM users WHERE department_id = :deptId",
        User.class)
    .setParameter("deptId", 5)
    .getResultList();

// Для сложных или нестандартных результатов используется ResultSetMapping
List<Object[]> results = session.createNativeQuery(
        "SELECT u.name, COUNT(o.id) FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id")
    .getResultList();

Когда использовать нативный запрос:Сложные аналитические запросы с оконными функциями, сложными GROUP BY. ✅ Вызов хранимых процедур или специфичных функций СУБД.Критичные к производительности операции, где оптимизированный вручную SQL дает значительный выигрыш. ✅ Работа с legacy-схемами, которые сложно или невозможно идеально отобразить на сущности.

Риски и ограничения:Потеря переносимости: Запрос привязан к синтаксису конкретной СУБД. ❌ Риск SQL-инъекций: Всегда используй параметризованные запросы (как в примере с :deptId). ❌ Обход кэша Hibernate: Может привести к несогласованности данных. ❌ Ручной маппинг: Часто требуется дополнительная работа по преобразованию ResultSet в объекты.

Ответ 18+ 🔞

Вот, смотри, как обстоят дела с этими вашими нативными запросами в Hibernate. Представь, что Hibernate — это такой заботливый, но слегка занудный переводчик. Он всё хочет перевести на свой HQL/JPQL, чтобы было красиво и независимо от базы данных. А иногда тебе надо просто крикнуть: «Отъебись, я сам знаю, как сказать!» Вот это и есть нативный запрос — чистый, голый SQL, который ты суёшь прямиком в базу, минуя все эти философские размышления фреймворка.

Как это, блядь, делается:

// Самый простой случай: говоришь — вот SQL, вот класс сущности, мапь, сука, обратно в объекты.
List<User> users = session.createNativeQuery(
        "SELECT * FROM users WHERE department_id = :deptId",
        User.class)
    .setParameter("deptId", 5) // Смотри, параметры! Без них ты — говнокодер, открывающий дверь SQL-инъекциям.
    .getResultList();

// А вот когда результат — какая-то ебеня из нескольких колонок или агрегатов.
// Тут уже Hibernate руками разводит: «Ой, а это во что мапить-то?».
// И приходится брать голый Object[] и самому ковыряться.
List<Object[]> results = session.createNativeQuery(
        "SELECT u.name, COUNT(o.id) FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id")
    .getResultList();

Так когда же надо выёбываться и писать SQL самому?

Когда запрос сложнее, чем твои жизненные проблемы. Оконные функции, хитрые GROUP BY, которые на HQL выглядят как попытка объяснить квантовую физику мартышке. ✅ Надо дернуть хранимую процедуру или какую-нибудь специфичную функцию базы вроде GEOGRAPHY_ПОШЛО_ВСЁ_НАХУЙ. ORM такое часто не умеет. ✅ Перформанс ебёт твою мать. Иногда, посмотрев на план запроса, который сгенерил Hibernate, хочется плакать. А твой ручной, отполированный запрос летает. ✅ Работаешь с легаси-схемой, которую проектировал пидарас шерстяной. Таблицы TBL_123_USR_A, колонки USR_NM_VC, связи через три левых джойна по дате. Мапить это на сущности — тот ещё геморрой. Проще написать SQL и забыть.

Но предупреждаю, тут подводных ебуч везде овердохуища:

Прощай, переносимость! Написал кучу SQL под PostgreSQL? Попробуй запустить это на Oracle. Правильно, получишь Syntax error, идиот. Придётся переписывать. ❌ SQL-инъекции — это пиздец. Я не шучу. Если ты вставляешь значения в строку через конкатенацию ("WHERE name = '" + name + "'"), то ты — манда с ушами, и твой сервис скоро будут использовать для майнинга крипты. Всегда. Используй. Параметры. Как в примере выше. ❌ Кэш Hibernate может нихуя не понять. Ты проапдейтил данные напрямую в базе нативным запросом, а Hibernate тебе из своего кэша старые значения подсовывает. Волнение ебать, отладка на три часа. ❌ Маппинг — твоя головная боль. Hibernate за тебя связные сущности (User.orders) не подтянет. Получил голые колонки — сам парсь, сам создавай объекты. Самый весёлый цирк начинается, когда структура результата не совпадает ни с одной сущностью.

Короче, инструмент мощный, но как бензопила — в руках идиота оставит от проекта рваные ошмётки. Используй с умом, только когда ORM реально не справляется или когда оптимизация — вопрос жизни и смерти. А так — живи спокойно, пусть Hibernate сам всё транслирует, это обычно надёжнее.