ORM. Расширенные темы 1 Embedded объекты Embedded объект – это объект, который не имеет собственной identity, а зависит от другой сущности В табличном представлении сама сущность и ее embedded сущность представляют одну строку таблицы. Физически не разделены Embedded сущности нужны, когда логически данные представляют собой отношение, а не просто коллекцию атрибутов, но БД хранит их физически в одной записи, по той или иной причине 2 Пример. Embedded объекты 3 Embedded объекты Embedded сущность помечается аннотацией @Embeddable Embeddable сущность может содержать простые мэпинги: @Basic, @Temporal, @Enumerated, @Lob и @Column Embeddable сущность не может содержать мэпинги отношений Embedded сущность определяется в целевой сущности с помощью @Embedded При работе с целевой сущностью, провайдер опрашивает поля embedded сущности так, как будто эти поля определены на целевой сущности Необходимо рассмотреть возможность переопределения embedded сущности в обычную сущность, если модель данных допускает модификацию 4 Пример. Embedded объекты 5 Совместное использование Embedded объектов Возможна ситуация, когда две или более сущностей используют одинаковый embedded объект Так же возможна ситуация логически эквивалентных embedded объектов, но соответствующие им колонки таблиц физически имеют разные названия 6 Совместное использование Embedded объектов Можно использовать аннотацию @AttributeOverride, чтобы переопределить поле(я) embedded сущности При совместном использовании embedded сущности (класса) будет создано соответствующее количество экземпляров этого класса 7 Пример. Совместное использование Embedded объектов 8 Составной первичный ключ Первичный ключ таблицы БД использует несколько колонок. Распространено в legacy БД Необходимо создать дополнительный primary key класс Primary key класс должен: Быть public и имплементировать Serializable корректно переопределить методы equals() и hashCode() Иметь конструктор без параметров 9 Составной первичный ключ. Id класс Одним из видов primary key класса является Id класс Пример. Составной первичный ключ. Id класс (example_09-01) Primary key класс должен содержать атрибуты, совпадающие (по имени и типу) с primary key атрибутами целевой сущности Primary key класс может не иметь setter’ов. Провайдер установит поля один раз через reflection Primary key класс является удобной абстракцией ключа: 10 Составной первичный ключ. Embedded Id класс В качестве primary key класса может выступать embedded класс В этом случае embedded класс в целевой сущности аннотируется с помощью @EmbeddedId Пример. Составной первичный ключ. Embedded Id класс (example_09-02) В запросах можно указывать путь, переходящий на поля embedded id класса, используя идентификатор id: 11 Read-only мэпинги JPA позволяет установить insertable и updatable свойства на индивидуальные поля состояния (@Column) и поля отношений (@JoinColumn) Можно установить значение false для insertable и/или updatable, в предположении, что данные уже существуют в БД Поля являются writable в памяти. Они не будут записаны в БД при flush сущности 12 Пример. Read-only мэпинги 13 Опциональность Можно указать провайдеру на опциональноть полей или single-valued ассоциаций Они могут быть null в БД На них не будет наложен not null constraint при генерации схемы Элемент optional может быть указан для: @Basic @ManyToOne @OneToOne По умолчанию optional имеет значение true. Можно указать false 14 Пример. Опциональность 15 Отношения. Расширенные темы В случае, когда объектная модель может диктовать схему БД, сложные мэпинги не понадобятся В случае, когда БД уже существует и ее схему менять нельзя, необходимо иметь возможность не «стандартного» мэпинга 16 Составные join колонки В случае наличия compound ключа, может возникнуть возможность ссылки на него Аннотация @JoinColumns служит для хранения нескольких join колонок Таблица EMPLOYEE с compound ключом, ссылающимся на себя 17 Пример. Составные join колонки 18 Идентификаторы, которые включают отношение Возможно иметь compound ключ, который включает отношение. Этим подразумевается, что объект не может существовать без другого, являющегося частью идентификатора Используем primary key класс @JoinColumn ссылается на колонку, уже являющуюся частью первичного ключа Пример. Идентификаторы, которые включают отношение (example_09-03) 19 Мэпинг состояния отношения (Relationship state) В примере дату начала работы сотрудника над проектом нельзя хранить ни в employee, ни в project, так как имеем отношение many-to-many В БД все просто: 20 Мэпинг состояния отношения (Relationship state) В Java состояние определяется как обычный мэпинг Объект состояния будет иметь Many-to-one мэпинг на целевые сущности (проект и работник) Первичный ключ объекта состояния будет compound, состоящий из двух отношений на целевые сущности Пример. Mapping Relationship state (example_09-04) 21 Несколько таблиц До сих пор мы рассматривали мэпинги, когда сущность мэпилась на один ряд одной таблицы! Это не всегда так в legacy БД, где логически связанные данные могут размещаться в разных рядах разных таблиц (по разным причинам) Таблица, для которой соответствующая сущность, аннотирована @Table называется primary table Таблица(ы), которой соответствуют связанные сущности, называется secondary table(s) 22 Несколько таблиц Сущность аннотируется @SecondaryTable Когда часть данных primary сущности лежит в secondary сущности, необходимо указать эту secondary сущность в элементе table аннотаций @Column и @JoinColumn Для связывания primary таблицы и secondary таблиц, в аннотации @SecondaryTable служит аннотация @PrimaryKeyJoinColumn Поддержка для связывания secondary таблиц ограничивается объединением первичного и вторичного ключей 23 Пример. Несколько таблиц 24 Пример. Несколько таблиц 25 Обзорный пример. Несколько таблиц. 26 Наследование Очень часто при ОО дизайне наследование используется неправильно – лишь для того, чтобы повторно использовать несколько методов О особой осторожностью необходимо использовать отношения наследования при O-R мэпинге 27 Пример. Наследование 28 Mapped супер класс JPA позволяет указывать mapped супер класс – класс не являющийся сущностью относительно него нельзя выполнять запросы он не может быть target сущностью Mapped супер класс может быть абстрактным или конкретным. Рекомендуется делать его абстрактным Mapped супер класс определяет часть состояния, которое недоступно непосредственно, но доступно сущностям, являющимся подклассами Mapped супер класс аннотируется @MappedSuperclass 29 Пример. Mapped супер класс 30 Transient классы в иерархии Класс в иерархии, не являющийся ни сущностью, ни mapped супер классом называется transient классом Состояние transient класса будет доступно подклассу (сущности или mapped супер классу), но это состояние не будет частью persistent состояния Transient класс удобно иметь для хранения временного состояния, промежуточных вычислений и т.п. 31 Пример. Transient классы в иерархии 32 Абстрактные и конкретные класс Сущность, mapped супер класс и transient класс могут быть абстрактными или конкретными Best practice делать mapped супер класс и transient абстрактными Делать абстрактной сущность может быть разумно, в случае, если данная сущность явно не используется в приложении, а используются ее подклассы Можно успешно выполнять JPQL запросы для абстрактной сущности – результатом будет набор сущностей, представляющих подклассы 33 Модели наследования JPA поддерживает 3 модели наследования Когда существует иерархия, она обязана иметь корневую сущность Корневая сущность аннотируется с помощью аннотации @Inheritance, в качестве атрибута которой указывается тип модели наследования 34 Стратегия Single-Table Наиболее распространенная и быстрая схема Набор полей всех подклассов хранится в одной строке одной таблицы В строке содержатся параметры, значимые в контексте одного подкласса, и бессмысленные в контексте другого Это означает, что некоторые колонки строки будут незаполненными Первичный ключ указывается в корневой сущности и наследуется подклассами Стратегия Single-Table является стратегией по умолчанию и определяется с помощью @Inheritance(strategy=InheritanceType.SINGLE_TABLE) 35 Стратегия Single-Table. Discriminator Column Специальная колонка, не участвующая в мэпинге, служащая для разделения иерархии, называется discriminator column Обозначается аннотацией @DiscriminatorColumn с указанием имени discriminator колонки. По умолчанию имя DTYPE Элемент discriminatorType определяет тип колонки: String (по умолчанию) Integer Char 36 Стратегия Single-Table. Discriminator Value Каждый ряд таблицы должен иметь значение, в discriminator колонке, называемое discriminator value или индикатор класса Соответствующее значение должно быть указано на классе в иерархии, с помощью @DiscriminatorValue По умолчанию в качестве discriminator value используется имя сущности Discriminator value может указываться только на конкретных сущностях 37 Пример. Стратегия Single-Table. Скриншот 193 38 Пример. Стратегия Single-Table. 39 Joined стратегия С точки зрения OO дизайна, разумно использовать отдельную таблицу для каждого класса в иерархии Хорошо нормализованная БД и хорошо декомпозированная ОО модель подходят под эту парадигму, но необходимо объединять таблицы. Такая стратегия называется Joined Strategy Однако, возможна потеря производительности Для связывания secondary таблиц используется foreign ключ, являющийся первичным ключом primary таблицы 40 Joined стратегия Необходимо явно указать стратегию @Inheritance(strategy=InheritanceType.JOINED) Discriminator колонка по-прежнему необходима 41 Пример. Joined стратегия С 194 скриншот 42 Стратегия одна таблица на одну сущность Такая модель данных как правило будет означать дублирование данных и не нормализованную БД Данная стратегия потребуется в будущих релизах JPA, в данном релизе малопригодна Пример. Стратегия одна таблица на одну сущность (example 09-01) 43