Стратегии наследования в 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;
}

Как выбирать

Сначала ответить, нужны ли запросы по базовому типу. Наследование сущностей подходит не всегда: композиция и отдельные таблицы часто проще мигрируют и лучше отражают агрегаты.

Вопросы для повторения

  1. Какая стратегия обычно быстрее для полиморфного чтения? SINGLE_TABLE, но ценой разреженной таблицы.
  2. Чем @MappedSuperclass отличается от JOINED? Базовый тип не является сущностью и не имеет таблицы.
  3. Почему TABLE_PER_CLASS сложна? Общий запрос требует объединения таблиц, а ключи должны быть уникальны по иерархии.
  4. Можно ли сменить стратегию без миграции? Нет, физическая схема и данные меняются.

Связано: Маппинг Hibernate, Кейс фильтр поверх UNION, Наследование.