Для чего используется аннотация @MappedSuperclass в Hibernate
Что проверяют
Отличаете ли вы переиспользование полей от наследования сущностей. Следом обычно просят сравнить с @Inheritance и спрашивают, можно ли сделать @ManyToOne на базовый класс.
Как отвечать
@MappedSuperclass это аннотация JPA (сейчас jakarta.persistence.MappedSuperclass: Hibernate 6 и Spring Boot 3 работают с пакетом jakarta, а не javax). Она помечает класс, чьи поля вместе с маппингом наследуют сущности, но сам класс сущностью не становится:
- таблицы нет: колонки базового класса физически повторяются в таблице каждого наследника;
- запросов нет: по стандарту JPA на такой класс нельзя сослаться в JPQL, передать его в
em.find()или сделать целью связи; - типичное содержимое:
@Idс генерацией,@Versionдля оптимистической блокировки, поля аудитаcreatedAtиupdatedAt. В Spring Data JPA сюда же вешают@EntityListeners(AuditingEntityListener.class)с@CreatedDateи@LastModifiedDate; - переопределение: если в каком-то наследнике колонка должна называться иначе, есть
@AttributeOverride, для связей@AssociationOverride.
Затем сравните с наследованием сущностей. @Inheritance со стратегиями SINGLE_TABLE (по умолчанию), JOINED и TABLE_PER_CLASS нужен, когда родитель сам по себе сущность и нужны полиморфные запросы: «дай все платежи», где платёж бывает картой или переводом. @MappedSuperclass нужен, когда общего у классов только технические поля и запрашивать их вместе никто не будет. Альтернатива через композицию: @Embeddable и @Embedded.
Где спотыкаются
Говорят, что это «то же самое, что TABLE_PER_CLASS». Нет: там родитель остаётся сущностью, по нему работают запросы и связи, а у @MappedSuperclass ничего этого нет.
Пишут в базовом классе equals() и hashCode() по сгенерированному id. До сохранения он null, после сохранения меняется, и сущность в HashSet ведёт себя непредсказуемо.
Не знают про побочный эффект генерации: GenerationType.IDENTITY в базовом классе отключает в Hibernate пакетную вставку JDBC сразу для всех наследников.