4.2.3. Представления
При извлечении сущностей из базы данных обычно встает вопрос - как обеспечить загрузку связанных сущностей на нужную глубину?
Например, для браузера Заказов нужно отобразить дату и сумму заказа совместно с названием Покупателя, т.е. загрузить связанный экземпляр Покупателя. А для экрана редактирования Заказа необходимо загрузить еще и коллекцию Пунктов заказа, причем каждый Пункт заказа должен содержать связанный экземпляр Товара для отображения его наименования.
Загрузка по требованию в большинстве случаев не может помочь, так как обработка данных, как правило, происходит не в транзакции, в которой загружаются сущности, а, например, на клиентском уровне в пользовательском интерфейсе. В то же время задание жадной загрузки в аннотациях сущностей недопустимо, так как приводит к постоянному извлечению всего графа связанных сущностей, который может быть очень большим.
Другой похожей проблемой является ограничение набора локальных атрибутов сущностей загружаемого графа: например, некоторая сущность имеет 50 атрибутов, в том числе BLOB, а в экране отображается только 10 атрибутов. Зачем загружать из БД, затем сериализовать и передавать клиенту 40 атрибутов, которые ему в данный момент не нужны?
Механизм представлений (views) решает эти проблемы, обеспечивая извлечение из базы данных и передачу клиенту графов сущностей, ограниченных в глубину и по атрибутам. Представление является описателем графа объектов, который требуется в некотором экране UI или другом процессе обработки данных.
Обработка представлений производится следующим образом:
-
Все связи в модели данных объявляются с признаком загрузки по требованию (
fetch = FetchType.LAZY
, см. Аннотации сущностей). -
В процессе загрузки данных через DataManager клиентский код помимо JPQL-запроса указывает нужное представление.
-
На основе представления формируется так называемая FetchGroup - особенность лежащего в основе слоя ORM фреймворка EclipseLink. Fetch Group влияет на формирование SQL-запроса к базе данных: как на список возвращаемых полей, так и на соединения с другими таблицами, содержащими связанные сущности.
![View](./img/View.png)
Представление определяется экземпляром класса View
, в котором:
-
entityClass
- класс сущности, для которого определено представление. Другими словами, "корень" дерева загружаемых сущностей. -
name
- имя представления. Должно быть либоnull
, либо уникальным в пределах данной сущности. -
properties
- коллекция экземпляров класса ViewProperty, соответствующих загружаемым атрибутам сущности. -
includeSystemProperties
- признак включения системных атрибутов (входящих в состав базовых интерфейсов персистентных сущностейBaseEntity
иUpdatable
).
Класс ViewProperty
имеет следующие свойства:
-
name
- имя атрибута сущности -
view
- для ссылочных атрибутов задает представление, с которым необходимо загружать связанную сущность -
fetch
- для ссылочных атрибутов задает способ загрузки связанной сущности из БД. Соответствует перечислениюFetchMode
:-
AUTO
- платформа автоматически выбирает оптимальный режим в зависимости от типа отношения. -
UNDEFINED
- загрузка будет выполнена по правилам JPA, что означает загрузку отдельными SELECT-запросами. -
JOIN
- загрузка в том же SELECT-запросе путем объединения с таблицей, содержащей ссвязанную сущность. -
BATCH
- загрузка экземпляров связанной сущности будет осуществляться порциями. Подробнее см. здесь.
Если атрибут
fetch
не указан, будет использоваться режимAUTO
. -
Tip
|
Независимо от набора атрибутов, определенного в представлении, всегда загружаются следующие атрибуты:
|
Warning
|
При попытке прочитать или установить значение незагруженного (не включенного в представление) атрибута генерируется исключение. Проверить, загружен ли некоторый атрибут можно методом |
Незагруженные атрибуты имеют значение null
. По умолчанию попытка установки значения незагруженного атрибута (вызов setter) для Detached сущности вызывает исключение.
Следует иметь в виду, что незагруженные ссылочные атрибуты Detached сущности, соответствующие внешним ключам (т.е. ManyToOne, OneToOne), можно установить в новое ненулевое значение в любом случае, и изменения будут сохранены при последующем merge()
.