Які типи наслідування підтримує JPA
JPA підтримує три стратегії наслідування сутностей, кожна з яких по-різному мапить ієрархію класів на таблиці бази даних. Вибір стратегії впливає на продуктивність, нормалізацію...
Огляд
JPA підтримує три стратегії наслідування сутностей, кожна з яких по-різному мапить ієрархію класів на таблиці бази даних. Вибір стратегії впливає на продуктивність, нормалізацію та складність запитів.
🟢 Junior Level
3 стратегії наслідування
JPA підтримує 3 стратегії наслідування:
- SINGLE_TABLE — всі класи в одній таблиці
- JOINED — кожен клас в своїй таблиці з JOIN
- TABLE_PER_CLASS — кожен клас в своїй таблиці (без JOIN)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public class Payment {
@Id
private Long id;
private BigDecimal amount;
}
@Entity
@DiscriminatorValue("CARD")
public class CardPayment extends Payment {
private String cardNumber;
}
@Entity
@DiscriminatorValue("CASH")
public class CashPayment extends Payment {
private LocalDate cashDate;
}
🟡 Middle Level
SINGLE_TABLE
Таблиця payments:
id | type | amount | card_number | cash_date
1 | CARD | 100 | 1234 | null
2 | CASH | 50 | null | 2024-01-01
✅ Швидко (немає JOIN)
✅ Прості запити
❌ Багато NULL для підкласів
❌ Не можна додати NOT NULL constraint
JOINED
Таблиця payments:
id | amount
1 | 100
2 | 50
Таблиця card_payments:
payment_id | card_number
1 | 1234
✅ Нормалізовано
✅ NOT NULL constraints можливі
❌ JOIN для кожного запиту
❌ Повільніше при вибірці
TABLE_PER_CLASS
Таблиця card_payments:
id | amount | card_number
1 | 100 | 1234
Таблиця cash_payments:
id | amount | cash_date
2 | 50 | 2024-01-01
✅ Немає NULL, немає JOIN для конкретного типу
❌ UNION для запиту батька
❌ Складні запити до батька
TABLE_PER_CLASS — optional в JPA spec. Не всі провайдери підтримують. Hibernate підтримує, але для portability перевіряйте ваш провайдер.
🔴 Senior Level
Порівняння стратегій
| Стратегія | INSERT | SELECT (тип) | SELECT (батько) | NULL | JOIN | Поліморфізм |
|---|---|---|---|---|---|---|
| SINGLE_TABLE | Швидко | Швидко | Швидко | Багато | Ні | ✅ |
| JOINED | Повільно | Повільно | Повільно | Ні | Так | ✅ |
| TABLE_PER_CLASS | Швидко | Швидко/Повільно | UNION | Ні | Ні | ⚠️ |
Коли що використовувати
SINGLE_TABLE:
✅ Маленька ієрархія (2-3 підкласи)
✅ Поля підкласів можуть бути NULL
✅ Висока продуктивність потрібна
JOINED:
✅ Нормалізація важлива
✅ Поля підкласів NOT NULL
✅ Рідкісні поліморфні запити
TABLE_PER_CLASS:
✅ Кожен підклас — незалежна сутність
✅ Поліморфні запити рідкісні
TYPE та TREAT в JPQL
// TYPE — фільтр по типу
@Query("SELECT p FROM Payment p WHERE TYPE(p) = CardPayment")
List<CardPayment> findCardPayments();
// TREAT — cast до підкласу
@Query("SELECT p FROM Payment p JOIN TREAT(p AS CardPayment) cp WHERE cp.cardNumber = :number")
List<Payment> findByCardNumber(@Param("number") String number);
@MappedSuperclass
@MappedSuperclass
public abstract class BaseEntity {
@Id
private Long id;
private LocalDateTime createdAt;
}
@Entity
public class CardPayment extends BaseEntity { }
@MappedSuperclass — НЕ entity: не можна робити запити до нього, немає таблиці, не можна використовувати в поліморфних запитах. Це просто шаблон для спільних полів.
Best Practices
✅ SINGLE_TABLE для простих випадків (2-3 підкласи)
✅ JOINED для нормалізації та NOT NULL
✅ TABLE_PER_CLASS для незалежних підкласів
✅ Уникати глибокої ієрархії (>3 рівнів)
✅ @MappedSuperclass коли не потрібен поліморфізм
❌ SINGLE_TABLE з багатьма підкласами
❌ JOINED для high-performance систем
❌ TABLE_PER_CLASS з частими поліморфними запитами
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- 3 стратегії: SINGLE_TABLE (одна таблиця), JOINED (таблиця на клас), TABLE_PER_CLASS (таблиця на клас, UNION для батька)
- SINGLE_TABLE — швидко (немає JOIN), але багато NULL, не можна NOT NULL constraint
- JOINED — нормалізовано, NOT NULL можливі, але JOIN для кожного запиту — повільно
- TABLE_PER_CLASS — немає NULL/JOIN для конкретного типу, але UNION для поліморфних запитів
- @MappedSuperclass — НЕ entity, немає таблиці, не можна поліморфні запити — шаблон для спільних полів
- TYPE для фільтру по типу, TREAT для cast до підкласу в JPQL
Пов’язані теми:
- [[26. Що таке JPQL і чим він відрізняється від SQL]]
- [[27. Що таке Criteria API і коли його використовувати]]
- [[23. Як правильно використовувати @OneToMany і @ManyToOne]]
- [[24. У чому особливості bidirectional relationships]]