Вы можете реализовать стратегию «соединенный подкласс», снабдив корневую сущность аннотацией @Inheritance, как показано в листинге 5.60 (код CD и Book останется таким же, как и раньше).
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
··@Id @GeneratedValue
··protected Long id;
··protected String title;
··protected Float price;
··protected String description;
··// Конструкторы, геттеры, сеттеры
}
С точки зрения разработчика, стратегия «соединенный подкласс» естественна, поскольку состояния всех сущностей, абстрактных или конкретных, будут отображаться в разные таблицы. На рис. 5.25 показано, как будут отображены сущности Item, Book и CD.
Рис. 5.25. Отображение наследования с применением стратегии «соединенный подкласс»
Вы по-прежнему сможете использовать аннотации @DiscriminatorColumn и @DiscriminatorValue в случае с корневой сущностью для настройки столбца дискриминатора и изменения значений (столбец DTYPE располагается в таблице ITEM).
Стратегия «соединенный подкласс» интуитивно понятна и близка к тому, что вы знаете, исходя из механизма объектного наследования. Однако выполнение запросов может влиять на производительность. В названии этой стратегии присутствует слово «соединенный», так как для повторной сборки экземпляра подкласса таблицу подкласса необходимо соединить с таблицей корневого класса. Чем глубже иерархия, тем больше соединений потребуется для сборки листовой сущности. Эта стратегия хорошо поддерживает полиморфные связи, однако требует, чтобы при создании экземпляров подклассов сущностей была проведена одна или несколько операций соединения. Это может привести к низкой производительности в случае с обширными иерархиями классов. Аналогичным образом запросы, которые охватывают всю иерархию классов, требуют проведения операций соединения между таблицами подклассов, приводящих к снижению производительности.
Если задействуется стратегия «таблица на класс» (или «таблица на конкретный класс»), то каждая сущность отображается в свою специально отведенную для этого таблицу, как при использовании стратегии «соединенный подкласс». Отличие состоит в том, что все атрибуты корневой сущности также будут отображены в столбцы таблицы дочерней сущности. С позиции базы данных эта стратегия денормализует модель и приводит к тому, что все атрибуты корневой сущности переопределяются в таблицах всех листовых сущностей, которые наследуют от нее. При стратегии «таблица на конкретный класс» нет совместно используемой таблицы, совместно используемых столбцов и столбца дискриминатора. Единственное требование состоит в том, что все таблицы должны совместно пользоваться общим первичным ключом — одинаковым для всех таблиц в иерархии.
Для отображения нашего примера с применением этой стратегии потребуется указать TABLE_PER_CLASS в аннотации @Inheritance (листинг 5.61) корневой сущности (Item).
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Item {
··@Id @GeneratedValue
··protected Long id;
··protected String title;
··protected Float price;
··protected String description;
··// Конструкторы, геттеры, сеттеры
}
На рис. 5.26 показаны таблицы ITEM, BOOK и CD. Как вы можете видеть, в BOOK и CD имеются дубликаты столбцов ID, TITLE, PRICE и DESCRIPTION таблицы ITEM. Обратите внимание, что показанные таблицы не связаны.
Рис. 5.26. Наличие дубликатов столбцов таблицы ITEM в таблицах BOOK и CD
Разумеется, помните, что каждая таблица может быть переопределена, если снабдить каждую сущность аннотацией @Table.
Стратегия «таблица на конкретный класс» хорошо работает при выполнении запросов к экземплярам одной сущности, поскольку ее применение схоже с использованием стратегии «одна таблица на класс»: запрос ограничивается одной таблицей. Недостаток этой стратегии заключается в том, что она делает полиморфные запросы в иерархии классов более затратными, чем другие стратегии (например, поиск всех элементов, включая CD-альбомы и книги). При применении этой стратегии запросы ко всем таблицам подклассов должны выполняться с использованием операции UNION, которая оказывается затратной, если охватывается большой объем данных. Поддержка этой стратегии в JPA 2.1 все еще необязательна.