Питання 29 · Розділ 16

Що таке projection в JPA

Projection — завантаження тільки потрібних полів сутності замість всього об'єкта. Це значно покращує продуктивність, зменшує обсяг даних в пам'яті та усуває необхідність dirty c...

Мовні версії: English Russian Ukrainian

Огляд

Projection — завантаження тільки потрібних полів сутності замість всього об’єкта. Це значно покращує продуктивність, зменшує обсяг даних в пам’яті та усуває необхідність dirty checking для read-only операцій.


🟢 Junior Level

Що таке projection

Projection — завантаження частини полів замість всієї сутності.

// Вся сутність — всі поля
SELECT u FROM User u

// Projection — тільки потрібні поля
SELECT u.name, u.email FROM User u

Навіщо використовувати

  • Менше даних з БД → швидший запит
  • Менше даних в пам’яті → економія
  • Немає dirty checking → менше overhead
  • Контрольований контракт API

Простий приклад

// DTO клас
public record UserDto(String name, String email) {}

// JPQL з конструктором
@Query("SELECT new com.example.UserDto(u.name, u.email) FROM User u")
List<UserDto> findUserDtos();

🟡 Middle Level

Типи projection

1. Array projection

@Query("SELECT u.name, u.email FROM User u")
List<Object[]> findNamesAndEmails();

Мінуси: type-unsafe, незручно

2. DTO projection (constructor expression)

public record UserDto(String name, String email) {}

@Query("SELECT new com.example.UserDto(u.name, u.email) FROM User u")
List<UserDto> findUserDtos();

Плюси: type-safe, зручно

3. Interface-based (Spring Data)

interface UserNameOnly {
    String getName();
    String getEmail();
}

List<UserNameOnly> findBy();

Плюси: Spring створює proxy через інтерфейс

Типові помилки

// ❌ Без повного імені класу
@Query("SELECT new UserDto(u.name, u.email) FROM User u")  // ❌

// ✅ З повним іменем
@Query("SELECT new com.example.UserDto(u.name, u.email) FROM User u")  // ✅

// ❌ Неправильний порядок аргументів
public record UserDto(String email, String name) {}  // email, name
@Query("SELECT new com.example.UserDto(u.name, u.email) FROM User u")  // ❌

🔴 Senior Level

Performance порівняння

Full entity:
- SELECT * → завантаження всіх полів
- Dirty checking overhead
- L1 cache storage

Projection:
- SELECT specific columns
- Немає dirty checking
- Менше пам'яті

Projection значно економить пам'ять (завантажуються тільки потрібні колонки)
і прискорює запит (менше даних по мережі).

Коли використовувати projection

✅ Projection для:
- Read-only операцій
- API відповідей
- Списків/таблиць
- Агрегації та статистики
- Великих наборів даних

❌ Повна сутність для:
- CRUD операцій
- Коли потрібні всі поля
- Коли потрібен dirty checking

Best Practices

✅ DTO projection для read-only
✅ Interface-based для гнучкості
✅ Class-based (record) для type-safety
✅ Повне ім'я класу в JPQL
✅ Правильний порядок аргументів

❌ Array projection (type-unsafe)
❌ Full entity для read-only

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Projection — завантаження тільки потрібних полів замість всієї сутності
  • 4 типи: Array (type-unsafe), DTO constructor (type-safe), Interface-based (Spring), Class-based (Spring)
  • Переваги: менше даних з БД, немає dirty checking overhead, контрольований API контракт
  • DTO projection вимагає повне ім’я класу: new com.example.UserDto(…)
  • Для read-only/API — projection, для CRUD — повні сутності

Пов’язані теми:

  • [[26. Що таке JPQL і чим він відрізняється від SQL]]
  • [[27. Що таке Criteria API і коли його використовувати]]
  • [[25. Як уникнути нескінченної рекурсії при серіалізації Entity]]
  • [[4. Що таке LazyInitializationException і як її уникнути]]