8. Какие три стратегии маппинга при наследовании сущностей (Entity Inheritance Mapping Strategies) описаны в JPA?
UNKNOWNСтратегии наследования нужны для того, чтобы дать понять провайдеру (Hibernate) как ему отображать в БД сущности-наследники. Для этого нам нужно декорировать родительский класс аннотацией @Inheritance и указать один из типов отображения: SINGLE_TABLE, TABLE_PER_CLASS, JOINED.
Следующие три стратегии используются для отображения данных сущности- наследника и родительской сущности:
- SINGLE_TABLE. Одна таблица на всю иерархию классов.
- TABLE_PER_CLASS. Таблица для каждого конкретного класса сущностей.
- JOINED. Стратегия «соединения», при которой поля или свойства, специфичные для подклассов, отображаются в таблицах этих подклассов, а поля или свойства родительского класса отображаются в таблице родительского класса.
Одна таблица на всю иерархию классов (SINGLE TABLE)
Является стратегией по умолчанию и используется, когда аннотация @Inheritance не указана в родительском классе или когда она указана без конкретной стратегии.

Все entity, со всеми наследниками записываются в одну таблицу. Для идентификации типа entity (наследника) определяется специальная колонка “discriminator column”. Например, если есть entity Employee c классами-потомками FullTimeEmployee и PartTimeEmployee, то при такой стратегии все FullTimeEmployee и PartTimeEmployee записываются в таблицу Employee, и при этом в таблице появляется дополнительная колонка с именем DTYPE, в которой будут записаны значения, определяющие принадлежность к классу. По умолчанию эти значения формируются из имён классов, в нашем случае - либо «FullTimeEmployee» либо «PartTimeEmployee». Но мы можем их поменять в аннотации у каждого класса-наследника: @DiscriminatorValue(\"F\").
Если мы хотим поменять имя колонки, то мы должны указать её новое имя в параметре аннотации у класса-родителя:
@DiscriminatorColumn(name=EMP_TYPE).
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Entity
@DiscriminatorColumn(name = \"EMP_TYPE\")
public class Employee {
@Id
@GeneratedValue
private long id;
private String name;
}
@Entity
@DiscriminatorValue(\"F\")
public class FullTimeEmployee extends Employee {
private int salary;
}
@Entity
@DiscriminatorValue(\"P\")
public class PartTimeEmployee extends Employee {
private int hourlyRate;
}
Эта стратегия обеспечивает хорошую поддержку полиморфных отношений между сущностями и запросами, которые охватывают всю иерархию классов сущностей:
-- Persisting entities --
FullTimeEmployee{id=0, name='Sara', salary=100000}
PartTimeEmployee{id=0, name='Tom', hourlyRate='60'}
-- Native queries --
'Select * from Employee'
[F, 1, Sara, null, 100000]
[P, 2, Tom, 60, null]
-- Loading entities --
FullTimeEmployee{id=1, name='Sara', salary=100000}
PartTimeEmployee{id=2, name='Tom', hourlyRate='60'}
Минусом стратегии является невозможность применения ограничения NOT NULL для тех колонок таблицы, которые характерны только для классов-наследников.
Стратегия «соединения» (JOINED)
В данной стратегии корневой класс иерархии представлен отдельной таблицей, а каждый класс-наследник имеет свою таблицу, в которой отображены только поля этого класса-наследника. То есть таблица подкласса не содержит столбцы для полей, унаследованных от родительского класса, за исключением поля для первичного ключа @Id, который должен быть определен только в родительской таблице.
Столбец первичного ключа в таблице подкласса служит внешним ключом первичного ключа таблицы суперкласса. Также в таблице родительского класса добавляется столбец DiscriminatorColumn с DiscriminatorValue для определения типа наследника.

@Inheritance(strategy = InheritanceType.JOINED)
@Entity
@DiscriminatorColumn(name = \"EMP_TYPE\") //определение типа наследника
public class Employee {
@Id
@GeneratedValue
private long id;
private String name;
.............
}
@Entity
@DiscriminatorValue(\"F\")
@Table(name = \"FULL_TIME_EMP\")
public class FullTimeEmployee extends Employee {
private int salary;
.............
}
@Entity
@DiscriminatorValue(\"P\")
@Table(name = \"PART_TIME_EMP\")
public class PartTimeEmployee extends Employee {
private int hourlyRate;
.............
} 23
-- Persisting entities --
FullTimeEmployee{id=0, name='Sara', salary=100000}
PartTimeEmployee{id=0, name='Robert', hourlyRate='60'}
-- Native queries --
'Select * from Employee'
[F, 1, Sara]
[P, 2, Robert]
'Select * from FULL_TIME_EMP'
[100000, 1]
'Select * from PART_TIME_EMP'
[60, 2]
Эта стратегия обеспечивает хорошую поддержку полиморфных отношений, но требует выполнения одной или нескольких операций соединения таблиц при создании экземпляров подклассов сущностей. В глубоких иерархиях классов это может привести к недопустимому снижению производительности. Точно так же запросы, которые покрывают всю иерархию классов, требуют операций соединения между таблицами подклассов, что приводит к снижению производительности:
-- Loading entities --
List<Employee> entityAList = em.createQuery(\"Select t from Employee t\")
.getResultList(); // Hibernate makes joins to assemble entities
FullTimeEmployee{id=1, name='Sara', salary=100000}
PartTimeEmployee{id=2, name='Robert', hourlyRate='60'}
Таблица для каждого класса сущностей (TABLE PER CLASS)
В соответствии со спецификацией Java Persistence API поддержка этой стратегии является необязательной и может поддерживаться не всеми провайдерами.
Каждый класс-наследник имеет свою таблицу. Во всех таблицах подклассов хранятся все поля этого класса плюс те, которые унаследованы от суперкласса.

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Entity
public class Employee {
@Id
@GeneratedValue
private long id;
private String name;
.............
}
@Entity
@Table(name = \"FULL_TIME_EMP\")
public class FullTimeEmployee extends Employee {
private int salary;
.............
}
@Entity
@Table(name = \"PART_TIME_EMP\")
public class PartTimeEmployee extends Employee {
private int hourlyRate;
.............
}
-- Persisting entities --
FullTimeEmployee{id=0, name='Sara', salary=100000}
PartTimeEmployee{id=0, name='Robert', hourlyRate='60'}
-- Native queries --
'Select * from Employee'
// no data
'Select * from FULL_TIME_EMP'
[1, Sara, 100000]
'Select * from PART_TIME_EMP'
[2, Robert, 60]
-- Loading entities --
List<Employee> entityAList = em.createQuery(\"Select t from Employee t\")
.getResultList(); // Hibernate makes additional sql- or union-queries to get
entities
PartTimeEmployee{id=2, name='Robert', hourlyRate='60'}
FullTimeEmployee{id=1, name='Sara', salary=100000}
Минусом является плохая поддержка полиморфизма (polymorphic relationships) и то, что для выборки всех классов иерархии потребуется большое количество отдельных sql-запросов для каждой таблицы-наследника или использование UNION- запроса для соединения таблиц всех наследников в одну таблицу.
Также недостатком этой стратегии является повторение одних и тех же атрибутов в таблицах.
При TABLE PER CLASS не работает стратегия генератора первичных ключей IDENTITY, поскольку может быть несколько объектов подкласса, имеющих один и тот же идентификатор, и запрос базового класса приведет к получению объектов с одним и тем же идентификатором (даже если они принадлежат разным типам).
Предыдущий вопрос: 4. Может ли абстрактный класс быть Entity?
Следующий вопрос: 9. Как мапятся Enum`ы?
Все вопросы по теме: список
Все темы: список
Вопросы/замечания/предложения/нашли ошибку: напишите мне