4.5.3. Источники данных

Источники данных обеспечивают работу связанных с данными (data-aware) компонентов.

Визуальные компоненты сами не обращаются к Middleware, а получают экземпляры сущностей из связанных источников данных. При этом один источник данных может обслуживать несколько визуальных компонентов, если им нужен один и тот же экземпляр или набор экземпляров.

Связь визуального компонента и источника данных проявляется в следующем:

  • При изменении пользователем значения в компоненте новое значение проставляется в атрибуте сущности, находящейся в источнике.

  • При изменении атрибута сущности из кода новое значение проставляется и отображается в визуальном компоненте.

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

  • При необходимости прочитать или записать значение атрибута сущности в коде предпочтительнее использовать источник данных, а не компонент. Рассмотрим пример чтения атрибута:

    @Inject
    private FieldGroup fieldGroup;
    
    @Inject
    private Datasource<Order> orderDs;
    
    public void init(Map<String, Object> params) {
        Customer customer;
        // Get customer from component
        customer = (Customer) fieldGroup.getFieldValue("customer");
        // Get customer from datasource
        customer = orderDs.getItem().getCustomer();
    }

    Как видно из примера, работа со значениями атрибутов сущностей через компонент требует приведения типа и, в случае FieldGroup, указания имени атрибута в виде строки. В то же время, получив методом getItem() из источника данных хранящийся в нем экземпляр, можно напрямую читать и изменять значения его атрибутов.

Как правило, визуальный компонент привязывается к атрибуту, непосредственно принадлежащему сущности, находящейся в источнике данных. В приведенном выше примере компонент привязан к атрибуту customer сущности Order.

Можно также привязать компонент к атрибуту связанной сущности, например к customer.name. В этом случае компонент будет корректно отображать значение атрибута name, но при его изменении пользователем слушатели источника данных вызваны не будут, и изменения не будут сохранены. Поэтому привязывать компонент к атрибутам второго и более порядка имеет смысл только для отображения, например в Label, колонке Table или установив для TextField свойство editable = false.

Источники данных также отслеживают изменения содержащихся в них сущностей, и могут отправлять измененные экземпляры обратно на Middleware для сохранения в базе данных.

Рассмотрим основные интерфейсы источников.

Рисунок 4.20. Интерфейсы источников данных

Интерфейсы источников данных

  • Datasource − простейший источник данных, предназначенный для работы с одним экземпляром сущности. Экземпляр устанавливается методом setItem() и доступен через getItem().

    Стандартной реализацией такого источника является класс DatasourceImpl, который используется, например, как главный источник данных в экранах редактирования сущностей.

  • CollectionDatasource − источник данных, предназначенный для работы с коллекцией экземпляров сущности. Коллекция загружается при вызове метода refresh(), ключи экземпляров доступны через метод getItemIds(). Метод setItem() устанавливает, а getItem() возвращает "текущий" экземпляр коллекции, т.е., например, соответствующий выбранной в данный момент строке таблицы.

    Способ загрузки коллекции сущностей определяется реализацией. Наиболее типичный - загрузка с Middleware через DataManager, при этом для формирования JPQL запроса используются методы setQuery(), setQueryFilter().

    Стандартной реализацией такого источника является класс CollectionDatasourceImpl, который используется в экранах, отображающих списки сущностей.

    • GroupDatasource − подвид CollectionDatasource, предназначенный для работы с компонентом GroupTable .

      Стандартной реализацией является класс GroupDatasourceImpl.

    • HierarchicalDatasource − подвид CollectionDatasource, предназначенный для работы с компонентами Tree и TreeTable .

      Стандартной реализацией является класс HierarchicalDatasourceImpl.

  • NestedDatasource - источник данных, предназначенный для работы с экземплярами, загруженными в атрибуте другой сущности. При этом источник, содержащий сущность-хозяина, доступен методом getMaster(), а мета-свойство, соответствующее атрибуту хозяина, содержащему экземпляры данного источника, доступно через метод getProperty().

    Например, в источнике dsOrder установлен экземпляр сущности Order, содержащий ссылку на экземпляр Customer. Тогда для связи экземпляра Customer с визуальными компонентами достаточно создать NestedDatasource, у которого хозяином является dsOrder, а мета-свойство указывает на атрибут Order.customer.

    • PropertyDatasource - подвид NestedDatasource, предназначенный для работы с одним экземпляром или коллекцией связанных сущностей, не являющихся встроенными (embedded).

      Стандартные реализации: для работы с одним экземпляром - PropertyDatasourceImpl, для работы с коллекцией - CollectionPropertyDatasourceImpl, GroupPropertyDatasourceImpl, HierarchicalPropertyDatasourceImpl. Последние реализуют также интерфейс CollectionDatasource, однако некоторые его нерелевантные методы, связанные с загрузкой, например, setQuery(), выбрасывают UnsupportedOperationException.

    • EmbeddedDatasource - подвид NestedDatasource, содержащий экземпляр встроенной сущности.

      Стандартной реализацией является класс EmbeddedDatasourceImpl.

  • RuntimePropsDatasource − специфический источник, предназначенный для работы с динамическими атрибутами сущностей.

Как правило, источники данных объявляются декларативно в секции dsContext дескриптора экрана.