Стратегии наследования в JPA
@Inheritance задаётся на корневой сущности. Выбор стратегии влияет на ограничения схемы и стоимость запросов по базовому типу.
SINGLE_TABLE
Вся иерархия хранится в одной таблице, конкретный тип определяется discriminator.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "kind")
abstract class Payment {}Плюсы: один запрос без join, простая полиморфная выборка. Минусы: много nullable-столбцов, ограничения NOT NULL для полей подтипа труднее выразить.
JOINED
Общие поля лежат в таблице базового класса, специфические — в таблицах наследников с тем же ключом.
Плюсы: нормализованная схема и ограничения. Минусы: запрос объекта требует join; выборка всей иерархии может стать тяжёлой.
TABLE_PER_CLASS
Каждый конкретный класс хранит и общие, и собственные поля в отдельной таблице.
Плюсы: запрос конкретного типа прост. Минусы: полиморфная выборка строится через UNION, общая генерация идентификаторов и ограничения усложняются.
@MappedSuperclass
Это не стратегия сущностей: базовый класс не имеет собственной таблицы и не участвует в полиморфных запросах. Его поля копируются в таблицы наследников.
@MappedSuperclass
abstract class Audited {
@Column(nullable = false)
Instant createdAt;
}Как выбирать
Сначала ответить, нужны ли запросы по базовому типу. Наследование сущностей подходит не всегда: композиция и отдельные таблицы часто проще мигрируют и лучше отражают агрегаты.
Вопросы для повторения
- Какая стратегия обычно быстрее для полиморфного чтения?
SINGLE_TABLE, но ценой разреженной таблицы. - Чем
@MappedSuperclassотличается отJOINED? Базовый тип не является сущностью и не имеет таблицы. - Почему
TABLE_PER_CLASSсложна? Общий запрос требует объединения таблиц, а ключи должны быть уникальны по иерархии. - Можно ли сменить стратегию без миграции? Нет, физическая схема и данные меняются.
Связано: Маппинг Hibernate, Кейс фильтр поверх UNION, Наследование.