8. Какие три стратегии маппинга при наследовании сущностей (Entity Inheritance Mapping Strategies) описаны в JPA?

8. Какие три стратегии маппинга при наследовании сущностей (Entity Inheritance Mapping Strategies) описаны в JPA?

UNKNOWN

Стратегии  наследования  нужны  для  того,  чтобы  дать  понять  провайдеру (Hibernate)  как  ему  отображать  в  БД  сущности-наследники.  Для  этого  нам  нужно декорировать  родительский  класс  аннотацией  @Inheritance  и  указать  один  из  типов отображения: SINGLE_TABLE, TABLE_PER_CLASS, JOINED.

Следующие  три  стратегии  используются  для  отображения  данных  сущности- наследника и родительской сущности:

  1. SINGLE_TABLE. Одна таблица на всю иерархию классов.
  2. TABLE_PER_CLASS. Таблица для каждого конкретного класса сущностей.
  3. 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`ы?

Все вопросы по теме: список

Все темы: список

Вопросы/замечания/предложения/нашли ошибку: напишите мне

Report Page