4.2.3. Представления

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

Например, для браузера Заказов нужно отобразить дату и сумму заказа совместно с названием Покупателя, т.е. загрузить связанный экземпляр Покупателя. А для экрана редактирования Заказа необходимо загрузить еще и коллекцию Пунктов заказа, причем каждый Пункт заказа должен содержать связанный экземпляр Товара для отображения его наименования.

Загрузка по требованию в большинстве случаев не может помочь, так как обработка данных, как правило, происходит не в транзакции, в которой загружаются сущности, а, например, на клиентском уровне в пользовательском интерфейсе. В то же время задание жадной загрузки в аннотациях сущностей недопустимо, так как приводит к постоянному извлечению всего графа связанных сущностей, который может быть очень большим.

Другой похожей проблемой является ограничение набора локальных атрибутов сущностей загружаемого графа: например, некоторая сущность имеет 50 атрибутов, в том числе BLOB, а в экране отображается только 10 атрибутов. Зачем загружать из БД, затем сериализовать и передавать клиенту 40 атрибутов, которые ему в данный момент не нужны?

Механизм представлений (views) решает эти проблемы, обеспечивая извлечение из базы данных и передачу клиенту графов сущностей, ограниченных в глубину и по атрибутам. Представление является описателем графа объектов, который требуется в некотором экране UI или другом процессе обработки данных.

Обработка представлений производится следующим образом:

  • Все связи в модели данных объявляются с признаком загрузки по требованию (fetch = FetchType.LAZY, см. Аннотации сущностей).

  • В процессе загрузки данных через DataManager клиентский код помимо JPQL-запроса указывает нужное представление.

  • На основе представления формируется так называемая FetchGroup - особенность лежащего в основе слоя ORM фреймворка EclipseLink. Fetch Group влияет на формирование SQL-запроса к базе данных: как на список возвращаемых полей, так и на соединения с другими таблицами, содержащими связанные сущности.

View
Рисунок 9. Классы представления

Представление определяется экземпляром класса View, в котором:

  • entityClass - класс сущности, для которого определено представление. Другими словами, "корень" дерева загружаемых сущностей.

  • name - имя представления. Должно быть либо null, либо уникальным в пределах данной сущности.

  • properties - коллекция экземпляров класса ViewProperty, соответствующих загружаемым атрибутам сущности.

  • includeSystemProperties - признак включения системных атрибутов (входящих в состав базовых интерфейсов персистентных сущностей BaseEntity и Updatable).

Класс ViewProperty имеет следующие свойства:

  • name - имя атрибута сущности

  • view - для ссылочных атрибутов задает представление, с которым необходимо загружать связанную сущность

  • fetch - для ссылочных атрибутов задает способ загрузки связанной сущности из БД. Соответствует перечислению FetchMode:

    • AUTO - платформа автоматически выбирает оптимальный режим в зависимости от типа отношения.

    • UNDEFINED - загрузка будет выполнена по правилам JPA, что означает загрузку отдельными SELECT-запросами.

    • JOIN - загрузка в том же SELECT-запросе путем объединения с таблицей, содержащей ссвязанную сущность.

    • BATCH - загрузка экземпляров связанной сущности будет осуществляться порциями. Подробнее см. здесь.

    Если атрибут fetch не указан, будет использоваться режим AUTO. Если атрибут представляет собой кэшируемую сущность, независимо от указанного значения будет использоваться UNDEFINED.

Tip

Независимо от набора атрибутов, определенного в представлении, всегда загружаются следующие атрибуты:

  • id - идентификатор сущности

  • version - для оптимистично блокируемых сущностей, реализующих Versioned

  • deleteTs, deletedBy - для сущностей, реализующих SoftDelete

Warning

При попытке прочитать или установить значение незагруженного (не включенного в представление) атрибута генерируется исключение. Проверить, загружен ли некоторый атрибут можно методом PersistenceHelper.isLoaded().

Незагруженные атрибуты имеют значение null. По умолчанию попытка установки значения незагруженного атрибута (вызов setter) для Detached сущности вызывает исключение.

Следует иметь в виду, что незагруженные ссылочные атрибуты Detached сущности, соответствующие внешним ключам (т.е. ManyToOne, OneToOne), можно установить в новое ненулевое значение в любом случае, и изменения будут сохранены при последующем merge().