Що таке JPQL і чим він відрізняється від SQL
JPQL (Java Persistence Query Language) — об'єктно-орієнтований мову запитів, який працює з сутностями та їх полями, а не з таблицями та колонками. Розуміння відмінностей між JPQ...
Огляд
JPQL (Java Persistence Query Language) — об’єктно-орієнтований мову запитів, який працює з сутностями та їх полями, а не з таблицями та колонками. Розуміння відмінностей між JPQL та SQL критично важливе для ефективної роботи з JPA.
🟢 Junior Level
Що таке JPQL
JPQL — мова запитів, яка працює з сутностями та полями, а не з таблицями та колонками.
// JPQL — працює з entity класами
List<User> users = entityManager.createQuery(
"SELECT u FROM User u WHERE u.age > :age", User.class)
.setParameter("age", 18)
.getResultList();
// SQL — працює з таблицями
// SELECT * FROM users WHERE age > 18;
Основні відмінності
| JPQL | SQL |
|---|---|
User (entity class) |
users (table name) |
u.age (field name) |
age (column name) |
u.userProfile.city (навігація) |
JOIN user_profiles ON ... |
| Database-agnostic | Database-specific |
| Автоматично генерує SQL | Прямий SQL |
Параметризація
// ✅ Named parameters (рекомендується)
@Query("SELECT u FROM User u WHERE u.name = :name AND u.age > :age")
List<User> find(@Param("name") String name, @Param("age") int age);
// ❌ String concatenation (SQL injection!)
"SELECT u FROM User u WHERE u.name = '" + name + "'" // ❌
🟡 Middle Level
JPQL vs SQL — детальне порівняння
JPQL:
- Працює з entities та полями
- Database-agnostic (працює з будь-якою БД)
- Автоматично генерує SQL
- Підтримує поліморфні запити
- Працює з наслідуванням
SQL:
- Працює з таблицями та колонками
- Database-specific (синтаксис залежить від БД)
- Повний контроль над запитом
- Немає автоматичного маппінгу
Native SQL — коли потрібен
// Database-specific функції
List<User> users = entityManager.createNativeQuery(
"SELECT * FROM users WHERE created_at > NOW() - INTERVAL '30 days'",
User.class
).getResultList();
// CTE (Common Table Expressions)
List<User> users = entityManager.createNativeQuery("""
WITH recent_orders AS (
SELECT user_id, MAX(created_at) as last_order
FROM orders GROUP BY user_id
)
SELECT u.* FROM users u JOIN recent_orders ro ON u.id = ro.user_id
WHERE ro.last_order > :date
""", User.class).setParameter("date", date).getResultList();
🔴 Senior Level
JPQL та наслідування
// Поліморфний запит — включає всі підкласи
SELECT p FROM Payment p // CardPayment, CashPayment, CryptoPayment
// З TYPE — фільтр по підкласу
SELECT p FROM Payment p WHERE TYPE(p) = CardPayment
// З TREAT — cast до підкласу
SELECT p FROM Payment p JOIN TREAT(p AS CardPayment) cp WHERE cp.cardNumber = :number
Коли використовувати JPQL vs SQL
JPQL:
✅ Database-agnostic запити
✅ Прості та середні запити
✅ Коли потрібна поліморфність
✅ Коли працюєте з entities
Native SQL:
✅ Database-specific функції (CTE, window functions)
✅ Складні аналітичні запити
✅ Bulk operations (UPDATE/DELETE тисяч записів)
✅ Коли JPQL не підтримує синтаксис
Best Practices
✅ JPQL для database-agnostic запитів
✅ Native SQL для database-specific запитів
✅ Parameterized queries (не concat!)
✅ DTO projection для read-only
✅ JOIN FETCH для пов'язаних даних
❌ Concat для параметрів (SQL injection!)
❌ JPQL для складних database-specific запитів
❌ Завантаження повних entities для read-only
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- JPQL працює з сутностями та полями, SQL — з таблицями та колонками
- JPQL database-agnostic (працює з будь-якою БД), SQL — database-specific
- Параметризація обов’язкова: :name (named parameters), НЕ string concat (SQL injection!)
- Native SQL потрібен для: CTE, window functions, bulk operations, database-specific функцій
- DTO projection в JPQL через конструктор: SELECT new com.example.Dto(…)
Пов’язані теми:
- [[27. Що таке Criteria API і коли його використовувати]]
- [[28. Як використовувати JOIN FETCH для вирішення проблеми N+1]]
- [[29. Що таке projection в JPA]]
- [[30. Які типи наслідування підтримує JPA]]