middle
HQL и JPQL — что это и чем отличаются от SQL?
JPQL (Jakarta Persistence Query Language) — объектно-ориентированный язык запросов, определённый спецификацией JPA. Работает с сущностями и их полями, а не с таблицами и столбцами. HQL (Hibernate Query Language) — расширение JPQL от Hibernate с дополнительными возможностями.
JPQL vs SQL
Пример
-- SQL: работает с таблицами
SELECT u.user_name, d.dept_name
FROM users u
JOIN departments d ON u.department_id = d.id
WHERE u.status = 'ACTIVE';
-- JPQL: работает с сущностями
SELECT u.name, u.department.name
FROM User u
WHERE u.status = com.example.UserStatus.ACTIVE
Основные возможности JPQL
Примеры запросов
// SELECT
List<User> users = em.createQuery(
"SELECT u FROM User u WHERE u.name LIKE :name", User.class)
.setParameter("name", "%John%")
.getResultList();
// JOIN
List<User> users = em.createQuery(
"SELECT u FROM User u JOIN u.department d WHERE d.name = :deptName", User.class)
.setParameter("deptName", "Engineering")
.getResultList();
// Агрегация
Long count = em.createQuery(
"SELECT COUNT(u) FROM User u WHERE u.status = :status", Long.class)
.setParameter("status", UserStatus.ACTIVE)
.getSingleResult();
// DTO-проекция (конструктор)
List<UserDto> dtos = em.createQuery(
"SELECT new com.example.UserDto(u.name, u.email) FROM User u", UserDto.class)
.getResultList();
// UPDATE (bulk)
int updated = em.createQuery(
"UPDATE User u SET u.status = :newStatus WHERE u.lastLogin < :date")
.setParameter("newStatus", UserStatus.INACTIVE)
.setParameter("date", cutoffDate)
.executeUpdate();
// DELETE (bulk)
int deleted = em.createQuery(
"DELETE FROM User u WHERE u.status = :status")
.setParameter("status", UserStatus.DELETED)
.executeUpdate();
Важное
- JPQL — стандарт JPA, HQL — расширение Hibernate; JPQL-код переносим между реализациями
- JPQL работает с сущностями, не таблицами:
FROM User u, неFROM users u - Навигация по связям через точку:
u.department.name→ Hibernate сгенерирует JOIN - Bulk UPDATE/DELETE обходят Persistence Context и L1/L2 Cache — после них нужен
em.clear()
Частые ошибки
- Конкатенация параметров в запросе — SQL Injection! Всегда используйте
setParameter() - Bulk UPDATE без
clear()— L1 Cache не знает об изменениях, данные в памяти устарели - Навигация по LAZY-связям в SELECT —
SELECT u.department.name FROM User uгенерирует JOIN, но если department LAZY и не загружен, может привести к N+1 в других сценариях - Забывать про fetch —
SELECT u FROM User uне загружает LAZY-связи; для них нужен JOIN FETCH
Как используется в 2026
- JPQL/HQL — основной инструмент для нетривиальных запросов в JPA-приложениях
- Spring Data
@Queryиспользует JPQL по умолчанию, native SQL — черезnativeQuery = true - Для type-safe запросов — Criteria API или QueryDSL
- HQL в Hibernate 6 получил новые функции: ARRAY, JSON, улучшенный синтаксис
На собеседовании: покажите разницу между SQL и JPQL: SQL работает с таблицами, JPQL — с сущностями. Упомяните навигацию через точку (u.department.name). Если спросят про безопасность — всегда setParameter(), никогда конкатенация. Про bulk-операции: они обходят Persistence Context, нужен clear().