From 86207b7bb81aec2e72526e45e00d259a66cc4014 Mon Sep 17 00:00:00 2001 From: Konstantin Krivopustov Date: Fri, 18 Jan 2013 11:10:24 +0000 Subject: [PATCH] Refs #1710 Cut all except language from Locale when searching for messages (doc) --- doc/content/manual/ru/chapter_framework.xml | 16944 +++++++++--------- doc/content/manual/ru/manual.xml | 9 + 2 files changed, 8482 insertions(+), 8471 deletions(-) diff --git a/doc/content/manual/ru/chapter_framework.xml b/doc/content/manual/ru/chapter_framework.xml index 5aac2c569f..b2f36d0d1d 100644 --- a/doc/content/manual/ru/chapter_framework.xml +++ b/doc/content/manual/ru/chapter_framework.xml @@ -1,8471 +1,8473 @@ - - - - Устройство платформы -
- Архитектура - В данной главе рассмотрена архитектура CUBA-приложений в различных разрезах: по уровням, блокам, модулям, и по используемым базовым проектам. -
- Уровни и блоки приложения - Платформа позволяет строить приложения по классической трехуровневой схеме: клиентский уровень, средний слой, база данных. Уровень отражает степень "удаленности" пользователя от хранимых данных. - В дальнейшем речь пойдет в основном о среднем слое и клиентах, поэтому для краткости выражение "все уровни" означает два этих уровня. - На каждом уровне возможно создание одного или нескольких блоков (units) приложения. Блок представляет собой обособленную исполняемую программу, взаимодействующую с другими блоками приложения. Средства платформы CUBA позволяют создавать блоки в виде веб-приложений и десктопных приложений. Разработка блоков для мобильных платформ на данный момент остается за рамками CUBA, однако такие блоки, созданные другими средствами, могут быть интегрированы со стандартными блоками приложения. -
- Уровни и блоки приложения - - - - - -
- - - Middleware - - Средний слой, содержащий основную бизнес-логику приложения и выполняющий обращения к базе данных. Представляет собой отдельное веб-приложение под управлением стандартного контейнера Java EE Web Profile. См. - - - - Web Client - - Основной блок клиентского уровня. Содержит интерфейс, предназначенный, как правило, для внутренних пользователей организации. Представляет собой отдельное веб-приложение под управлением стандартного контейнера Java EE Web Profile. Реализация пользовательского интерфейса основана на фреймворке Vaadin. См. - - - - Desktop Client - - Дополнительный блок клиентского уровня. Содержит интерфейс, предназначенный, как правило, для внутренних пользователей организации. Представляет собой десктопное Java-приложение, реализация пользовательского интерфейса основана на фреймворке Java Swing. См. - - - - Web Portal - - Дополнительный блок клиентского уровня. Содержит интерфейс для внешних пользователей. Может использоваться для интеграции с мобильными устройствами или сторонними приложениями. Представляет собой отдельное веб-приложение под управлением стандартного контейнера Java EE Web Profile. Реализация пользовательского интерфейса основана на фреймворке Spring MVC. См. - - - - Обязательным блоком любого приложения является средний слой - Middleware. Для реализации пользовательского интерфейса как правило используется один или несколько клиентских блоков, например Web Client и Web Portal. - Вышеперечисленные блоки являются стандартными, однако в комплексном приложении для разделения функциональности можно без труда создать произвольное количество как клиентских блоков, так и блоков среднего слоя. - Все клиентские блоки взаимодействуют со средним слоем одинаковым образом посредством протокола HTTP, что позволяет размещать средний слой произвольным образом, в том числе за сетевым экраном. Следует отметить, что при развертывании в простейшем случае среднего слоя и веб-клиента на одном сервере между ними организуется локальное взаимодействие в обход сетевого стека для снижения накладных расходов. -
-
- Модули приложения - Модуль – наименьшая структурная единица CUBA-приложения. Представляет собой один модуль проекта приложения и соответствующий ему JAR файл с исполняемым кодом. - Стандартные модули: - - global – включает в себя классы сущностей, интерфейсы сервисов и другие общие для всех уровней классы. Используется во всех блоках приложения. - - - core – реализация сервисов и всех остальных компонентов среднего слоя. Используется только на Middleware. - - - gui – общие компоненты универсального пользовательского интерфейса. Используется в Web Client и Desktop Client. - - - web – реализация универсального пользовательского интерфейса на Vaadin, а также другие специфичные для веб-клиента классы. Используется в блоке Web Client. - - - desktop – опциональный модуль – реализация универсального пользовательского интерфейса на Java Swing, а также другие специфичные для десктоп-клиента классы. Используется в блоке Desktop Client. - - - portal – опциональный модуль – реализация веб-портала на Spring MVC. - - -
- Модули приложения - - - - - -
-
-
- Базовые проекты - Функциональность платформы разделена на несколько так называемых базовых проектов: - - cuba – основной базовый проект, содержит всю функциональность, описанную в данном руководстве, плюс подсистему безопасности (управление пользователями и их доступом к данным) - - - reports – подсистема генерации отчетов - - - workflow – подсистема управления потоками работ со встроенным визуальным редактором бизнес-процессов - - - fts – подсистема полнотекстового поиска - - - charts – подсистема вывода диаграмм - - - ccpayments – подсистема работы с кредитными картами - - - bpmn – механизм исполнения бизнес-процессов по стандарту BPMN 2.0 - - - Создаваемое на основе платформы приложение может включать в себя функциональность базовых проектов путем объявления зависимостей от их артефактов. Зависимость от артефактов cuba является обязательной. Опциональные базовые проекты в свою очередь также зависят от cuba, и в принципе могут содержать зависимости между собой. -
- Зависимости между проектами - - - - - -
- Сплошными линиями изображены обязательные зависимости, пунктирными − опциональные. -
-
- Состав приложения - Вышеописанные архитектурные принципы напрямую отражаются на составе собранного приложения. Рассмотрим его на примере простого приложения sales, которое имеет 2 блока – Middleware и Web Client; и включает в себя функциональность базовых проектов cuba и reports. -
- Состав простого приложения - - - - - -
- На рисунке изображено содержимое некоторых каталогов сервера Tomcat с развернутым в нем приложением sales. - Блок Middleware реализован веб-приложением app-core, блок Web Client – веб-приложением app. Исполняемый код веб-приложений содержится в каталогах WEB-INF/lib в наборе JAR-файлов. Каждый JAR представляет собой результат сборки (артефакт) одного из модулей приложения или базового проекта. - Например, состав JAR-файлов веб-приложения среднего слоя app-core определяется тем, что блок Middleware состоит из модулей global и core, и приложение использует базовые проекты cuba и reports (в данном случае версии 4.0.0). -
-
-
- Общие компоненты - В данной главе рассмотрены компоненты платформы, общие для всех уровней приложения. -
- Модель данных - Предметная область моделируется в приложении с помощью взаимосвязанных классов Java, называемых классами сущностей или просто сущностями. - Сущности подразделяются на две категории: - - персистентные – экземпляры таких сущностей хранятся в таблицах базы данных - - - неперсистентные – экземпляры существуют только в оперативной памяти - - - Сущности характеризуются своими атрибутами. Атрибут соответствует полю класса и паре методов доступа (get / set) к полю. Чтобы атрибут был неизменяемым (read only), достаточно не создавать метод set. - Персистентные сущности могут включать в себя атрибуты, не хранящиеся в БД. В случае неперсистентного атрибута можно не создавать поле класса, ограничившись методами доступа. - Класс сущности должен удовлетворять следующим требованиям: - - наследоваться от одного из базовых классов, предоставляемых платформой (см. ниже) - - - иметь набор полей и методов доступа, соответствующих атрибутам сущностей - - - класс и его поля (или методы доступа при отсутствии для атрибута соответствующего поля) должны быть определенным образом аннотированы для работы JPA (в случае персистентной сущности) и фреймворка метаданных - - - для поддержки возможного расширения сущностей поля класса необходимо объявлять с модификатором protected, а не private - - - Поддерживаются следующие типы атрибутов сущностей: - - java.lang.String - - - java.lang.Boolean - - - java.lang.Integer - - - java.lang.Long - - - java.lang.Double - - - java.math.BigDecimal - - - java.util.Date - - - java.sql.Date - - - java.sql.Time - - - java.util.UUID - - - byte[] - - - enum - - - сущность - - -
- Базовые классы сущностей - Рассмотрим базовые классы и интерфейсы сущностей более подробно. -
- Базовые классы сущностей - - - - - -
- - - Instance – декларирует базовые методы работы с объектами предметной области: - - получение ссылки на мета-класс объекта - - - генерация имени экземпляра - - - чтение/установка значений атрибутов по имени - - - добавление слушателей, получающих уведомления об изменениях атрибутов - - - - - Entity – дополняет Instance понятием идентификатора сущности, причем Entity не определяет тип идентификатора, оставляя эту возможность наследникам - - - AbstractInstance – реализует логику работы со слушателями изменения атрибутов - - AbstractInstance хранит слушателей в коллекции WeakReference, т.е. при отсутствии внешних ссылок на добавленного слушателя, он будет немедленно уничтожен сборщиком мусора. Как правило, слушателями изменения атрибутов являются визуальные компоненты и источники данных UI, на которые всегда имеются ссылки из других объектов, поэтому проблема исчезновения слушателей не возникает. Однако если слушатель создается прикладным кодом и на него никто не ссылается естественным образом, необходимо кроме добавления в Instance сохранить его в некотором поле объекта. - - - - AbstractNotPersistentEntity – базовый класс неперсистентных сущностей с идентификаторами типа UUID. - - - BaseEntity – декларирует присущие всем персистентным сущностям методы работы с информацией о том, кто и когда создал экземпляр сущности в базе данных - - - BaseUuidEntity - реализует BaseEntity с типом идентификатора UUID и поддержкой JPA - - - Versioned – интерфейс сущностей, поддерживающих оптимистичную блокировку - - - Updatable – интерфейс сущностей, для которых требуется сохранять информацию о том, кто и когда изменял экземпляр в последний раз - - - SoftDelete – интерфейс сущностей, поддерживающих мягкое удаление - - - StandardEntity – наиболее часто используемый базовый класс персистентных сущностей, реализующий вышеперечисленные интерфейсы - - - При создании классов сущностей рекомендуется выбирать базовый класс по следующим правилам: - - если сущность не хранится в БД, наследуйте ее от AbstractNotPersistentEntity - - - если сущность встраиваемая - наследуйте ее от EmbeddableEntity - - - если сущность только создается в БД, никогда не изменяется, и мягкое удаление не требуется - наследуйте ее от BaseUuidEntity - - - если сущность ведет себя стандартным образом: изменяется в БД, требует оптимистичной блокировки и мягкого удаления − наследуйте ее от StandardEntity - - - в противном случае наследуйте сущность от BaseUuidEntity и реализуйте в классе тот набор интерфейсов Versioned, Updatable, SoftDelete, который требуется - - - -
-
- Аннотации сущностей - В данном разделе описаны все поддерживаемые платформой аннотации классов и атрибутов сущностей. - Аннотации пакета javax.persistence обеспечивают работу JPA, аннотации пакетов com.haulmont.* предназначены для управления метаданными и другими механизмами платформы. - Если для аннотации указано только простое имя класса, подразумевается что это класс платформы, расположенный в одном из пакетов com.haulmont.* -
- Аннотации класса - - - - - @javax.persistence.Entity - - - - Объявляет класс сущностью модели данных. - Параметры: - - name - имя сущности, обязательно должно начинаться с префикса, отделенного знаком $. Желательно использовать в качестве префикса короткое имя проекта для формирования отдельного пространства имен. - - - Пример:@Entity(name = "sales$Customer") - - - - - - @javax.persistence.MappedSuperclass - - - - Определяет, что данный класс является предком некоторых сущностей, и его атрибуты должны быть использованы в составе сущностей-наследников. Такой класс не сопоставляется никакой отдельной таблице БД. - - - - - @javax.persistence.Table - - - Определяет таблицу базы данных для данной сущности. - Параметры: - - name - имя таблицы - - - Пример:@Table(name = "SALES_CUSTOMER") - - - - - - @javax.persistence.Embeddable - - - - Определяет встраиваемую сущность, экземпляры которой хранятся вместе с владеющей сущностью в той же таблице. - Для задания имени сущности тебуется применение аннотации - @MetaClass - . - - - - - - @javax.persistence.Inheritance - - - - Определяет стратегию наследования для иерархии классов сущностей. Данная аннотация должна быть помещена на корневом классе иерархии. - Параметры: - - strategy - стратегия, по умолчанию SINGLE_TABLE - - - - - - - @javax.persistence.DiscriminatorColumn - - - Используется для определения колонки БД, отвечающей за различение типов сущностей в случае стратегий наследования SINGLE_TABLE и JOINED. - Параметры: - - name - имя колонки-дискриминатора - - - discriminatorType - тип данных колонки-дискриминатора - - - Пример:@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER) - - - - - - @javax.persistence.DiscriminatorValue - - - - Определяет значение колонки-дискриминатора для данной сущности. Эта аннотация должна быть помещена на конкретном классе сущности. - Пример:@DiscriminatorValue("0") - - - - - - @javax.persistence.PrimaryKeyJoinColumn - - - - Используется в случае стратегии наследования JOINED для указания колонки внешнего ключа данной сущности, ссылающегося на первичный ключ сущности-предка. - Параметры: - - name - имя колонки внешнего ключа данной сущности - - - referencedColumnName - имя колонки первичного ключа сущности предка - - - Пример:@PrimaryKeyJoinColumn(name = "CARD_ID", referencedColumnName = "ID") - - - - - @NamePattern - - - Определяет способ получения имени экземпляра, возвращаемого методом Instance.getInstanceName(). - Значением аннотации должна быть строка вида {0}|{1}, где - - {0} - строка форматирования по правилам String.format(), или имя метода данного объекта с префиксом #. Метод должен возвращать String и не иметь параметров. - - - {1} - разделенный запятыми список имен полей класса, соответствующий формату {0}. В случае использования в {0} метода список полей все равно необходим, так как по нему формируется представление _minimal. - - - Примеры:@NamePattern("%s|name")@NamePattern("#getCaption|login,name") - - - - - @Listeners - - - Определяет список слушателей, предназначенных для реакции на события жизненного цикла экземпляров сущности на уровне Middleware. - Значением аннотации должна быть строка или массив строк с именами классов слушателей - см. - Строки используются здесь вместо ссылок на классы потому, что классы слушателей находятся только на уровне Middleware и не доступны клиентскому коду, в то время как классы самих сущностей используются на всех уровнях. - Примеры:@Listeners("com.haulmont.cuba.security.listener.UserEntityListener")@Listeners({"com.abc.sales.entity.FooListener","com.abc.sales.entity.BarListener"}) - - - - - @MetaClass - - - Используется для объявления неперсистентной или встраиваемой сущности (т.е. когда аннотация @javax.persistence.Entity не применима) - Параметры: - - name - имя сущности, обязательно должно начинаться с префикса, отделенного знаком $. Желательно использовать в качестве префикса короткое имя проекта для формирования отдельного пространства имен. - - - Пример:@MetaClass(name = "sys$LockInfo") - - - - - @SystemLevel - - - Указывает, что данная сущность является системной и не должна быть доступна для выбора пользователем в различных списках сущностей, например как тип параметра универсального фильтра или тип динамического атрибута. - - - - - @EnableRestore - - - Указывает, что экземпляры данной сущности доступны для восстановления после мягкого удаления в специальном экране core$Entity.restore. - - - - - @TrackEditScreenHistory - - - Указывает, что для данной сущности будет запоминаться история открытия экранов редактирования ({имя_сущности}.edit) с возможностью отображения в специальном экране sec$ScreenHistory.browse. - - - - - @Extends - - - Указывает, что данная сущность является расширением и должна повсеместно использоваться вместо базовой. См. - - - -
-
- Аннотации атрибутов - Аннотации атрибутов устанавливаются на соответствующие поля класса, за одним исключением: если требуется объявить неизменяемый (read only) неперсистентный атрибут foo, то достаточно создать метод доступа getFoo() и поместить на этот метод аннотацию @MetaProperty. - - - - - @javax.persistence.Transient - - - - Указывает, что данное поле не хранится в БД, т.е. является неперсистентным. - Поля поддерживаемых JPA типов (см. http://docs.oracle.com/javaee/5/api/javax/persistence/Basic.html) по умолчанию являются персистентными, поэтому аннотация @Transient обязательна для объявления неперсистентного атрибута такого типа. - Для включения @Transient атрибута в метаданные, необходимо также указать аннотацию - @MetaProperty - . - - - - - @org.apache.openjpa.persistence.Persistent - - - Указывает, что данное поле хранится в БД, т.е. является персистентным. - Данная аннотация требуется только для нестандартного для JPA типа поля, платформа на данный момент поддерживает один такой тип - java.util.UUID. Таким образом, @Persistent требуется только в одном случае - при объявлении персистентного поля типа UUID. - - - - - - @javax.persistence.Column - - - - Определяет колонку БД, в которой будут храниться значения данного атрибута. - Параметры: - - name - имя колонки - - - length - (необязательный параметр, по умолчанию 255) - длина колонки. Используется также при формировании метаданных и в конечном счете может ограничивать максимальныю длину вводимого текста в визуальных компонентах, работающих с данным атрибутом. - - - nullable - (необязательный параметр, по умолчанию true) - может ли атрибут содержать null. При указании nullable = false JPA контролирует наличие значения поля при сохранении, кроме того, визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. - - - - - - - - @javax.persistence.ManyToOne - - - - Определяет атрибут-ссылку на сущность с типом ассоциации много-к-одному. - Параметры: - - fetch - (по умолчанию EAGER) параметр, определяющий, будет ли JPA энергично загружать ассоциированную сущность. Данный параметр всегда должен быть установлен в значение LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. - - - optional - (необязательный параметр, по умолчанию true) - может ли атрибут содержать null. При указании optional = false JPA контролирует наличие ссылки при сохранении, кроме того, визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. - - - Например, несколько экземпляров Order (заказов) ссылаются на один экземпляр Customer (покупателя), в этом случае класс Order должен содержать следующее объявление:@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "CUSTOMER_ID") -protected Customer customer; - - - - - - @javax.persistence.OneToMany - - - - Определяет атрибут-коллекцию ссылок на сущность с типом ассоциации один-ко-многим. - Параметры: - - mappedBy - поле связанной сущности, определяющее ассоциацию - - - targetEntity - тип связанной сущности. Необязательный параметр, если коллекция объявлена с использованием Java generics. - - - fetch - (необязательный параметр, по умолчанию LAZY) - определяет, будет ли JPA энергично загружать коллекцию связанных сущностей. Необходимо всегда оставлять значение по умолчанию LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. - - - cascade - (необязательный параметр, по умолчанию {}) - каскадирование операций определяет, какие операции над сущностью должны быть применены к ассоциированным сущностям. Каскадирование на данном уровне не рекомендуется использовать. - - - Например, несколько экземпляров Item (пунктов заказа) ссылаются на один экземпляр Order (заказ) с помощью @ManyToOne поля Item.order, в этом случае класс Order может содержать коллекцию экземпляров Item:@OneToMany(mappedBy = "order") -protected Set<Item> items; - - - - - - @javax.persistence.OneToOne - - - - Определяет атрибут-ссылку на сущность с типом ассоциации один-к-одному. - Параметры: - - fetch - (по умолчанию EAGER) параметр, определяющий, будет ли JPA энергично загружать ассоциированную сущность. Данный параметр всегда должен быть установлен в значение LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. - - - mappedBy - поле связанной сущности, определяющее ассоциацию. Требуется устанавливать только на ведомой стороне ассоциации. - - - optional - (необязательный параметр, по умолчанию true) - может ли атрибут содержать null. При указании optional = false JPA контролирует наличие ссылки при сохранении, кроме того, визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. - - - Пример ведущей стороны ассоциации, класс Driver:@OneToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "CALLSIGN_ID") -protected DriverCallsign callsign; - Пример ведомой стороны ассоциации, класс DriverCallsign:@OneToOne(fetch = FetchType.LAZY, mappedBy = "callsign") -protected Driver driver; - - - - - - @javax.persistence.ManyToMany - - - - Определяет атрибут-коллекцию ссылок на сущность с типом ассоциации много-ко-многим. - Ассоциация много-ко-многим всегда имеет ведущую сторону и может иметь обратную сторону - ведомую. На ведущей строне указывается дополнительная аннотация @JoinTable, на ведомой стороне - параметр mappedBy. - Параметры: - - mappedBy - поле связанной сущности, определяющее ассоциацию с ведущей стороны. Необходимо указывать только на ведомой стороне. - - - targetEntity - тип связанной сущности. Необязательный параметр, если коллекция объявлена с использованием Java generics. - - - fetch - (необязательный параметр, по умолчанию LAZY) - определяет, будет ли JPA энергично загружать коллекцию связанных сущностей. Необходимо всегда оставлять значение по умолчанию LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. - - - Пример:TODO - - - - - - @javax.persistence.JoinColumn - - - - Используется для указания колонки БД, определяющей ассоциацию между сущностями. - Параметры: - - name - имя колонки - - - Пример:@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "CUSTOMER_ID") -protected Customer customer; - - - - - - @javax.persistence.JoinTable - - - - Используется для указания таблицы связи на ведущей стороне @ManyToMany ассоциации. - Параметры: - - name - имя таблицы связи - - - joinColumns - элемент @JoinColumn, определяющий колонку таблицы связей, соответствующую первичному ключу ведущей стороны ассоциации (т.е. содержащей аннотацию @JoinTable) - - - inverseJoinColumns - элемент @JoinColumn, определяющий колонку таблицы связей, соответствующую первичному ключу ведомой стороны ассоциации - - - Пример атрибута customers класса Group, являющегося ведущей стороной ассоциации:@ManyToMany -@JoinTable(name = "SALES_CUSTOMER_GROUP_LINK", - joinColumns = @JoinColumn(name = "GROUP_ID"), - inverseJoinColumns = @JoinColumn(name = "CUSTOMER_ID")) -protected Set<Customer> customers; - Пример атрибута groups класса Customer, являющегося ведомой стороной этой же ассоциации:@ManyToMany(mappedBy = "customers") -protected Set<Group> groups; - - - - - - @javax.persistence.OrderBy - - - - Определяет порядок элементов в атрибуте-коллекции на момент извлечения из базы данных. Данную аннотацию необходимо задавать для упорядоченных коллекций, таких как List или LinkedHashSet для получения предсказуемого порядка следования элементов. - Параметры: - - value - строка, определяющая порядок, в формате:orderby_list::= orderby_item [,orderby_item]* -orderby_item::= property_or_field_name [ASC | DESC] - - - Пример:@OneToMany(mappedBy = "user") -@OrderBy("createTs") -protected List<UserRole> userRoles; - - - - - - @javax.persistence.Embedded - - - - Определяет атрибут типа встраиваемой сущности, в свою очередь аннотированной @Embeddable. - Пример:@Embedded -protected Address address; - - - - - - @javax.persistence.Temporal - - - - Для атрибута типа java.util.Date уточняет тип хранимого значения: дата, время или дата+время. - Параметры: - - value - тип хранимого значения: DATE, TIME, TIMESTAMP - - - Пример:@Column(name = "START_DATE") -@Temporal(TemporalType.DATE) -protected Date startDate; - - - - - - @javax.persistence.Version - - - - Указывает, что данное поле хранит версию для поддержки оптимистичной блокировки сущностей. - Применение такого поля необходимо при реализации классом сущности интерфейса Versioned (базовый класс StandardEntity уже содержит такое поле). - Пример:@Version -@Column(name = "VERSION") -private Integer version; - - - - - @MetaProperty - - - Указывает, что данный атрибут должен быть включен в метаданные. Данная аннотация может быть установлена как на поле класса, так и на метод доступа, в случае отсутствия соответствующего атрибуту поля. - Данная аннотация не обязательна для полей, снабженных следующими аннотациями пакета javax.persistence: @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded. Такие поля отражаются в метаданных автоматически. Поэтому @MetaProperty в основном применяется для определения неперсистентных атрибутов сущностей. - Параметры: - - mandatory - (необязательный параметр, по умолчанию false) - может ли атрибут содержать null. При указании mandatory = true визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. - - - Пример использования для поля:@Transient -@MetaProperty -protected String token; - Пример использования для метода:@MetaProperty -public String getLocValue() { - if (!StringUtils.isBlank(messagesPack)) { - return AppBeans.get(Messsages.class).getMessage(messagesPack, value); - } else { - return value; - } -} - - - - - @OnDelete - - - Определяет политику обработки связи в случае мягкого удаления сущности, содержащей данный атрибут. См. - Пример:@OneToMany(mappedBy = "group") -@OnDelete(DeletePolicy.CASCADE) -private Set<Constraint> constraints; - - - - - @OnDeleteInverse - - - Определяет политику обработки связи в случае мягкого удаления сущности с обратной стороны ассоциации. См. - Пример:@ManyToOne -@JoinColumn(name = "DRIVER_ID") -@OnDeleteInverse(DeletePolicy.DENY) -private Driver driver; - - - - - @Composition - - - Указывает на то, что связь является композицией - более тесным вариантом ассоциации. Это означает, что связанная сущность имеет смысл только как часть владеющей сущности, т.е. создается и удаляется вместе с ней. - Например список пунктов в заказе (класс Order содержит коллекцию экземпляров Item):@OneToMany(mappedBy = "order") -@Composition -protected List<Item> items; - - - - - @LocalizedValue - - - Служит для описания способа получения локализованного значения некоторого изменяющегося атрибута, которое возвращает метод MessageTools.getLocValue(). - Параметры: - - messagePack - явное указание пакета, из которого будет взято локализованное сообщение, например com.haulmont.cuba.core.entity - - - messagePackExpr - выражение в терминах пути к атрибуту, хранящему имя пакета, из которого будет взято локализованное сообщение, например proc.messagesPack. Путь начинается с атрибута текущей сущности. - - - Пример аннотации, означающей, что локализованное значение атрибута state будет взято из пакета, имя которого хранится в атрибуте messagesPack связанной сущности proc:@Column(name = "STATE") -@LocalizedValue(messagePackExpr = "proc.messagesPack") -protected String state; - -@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "PROC_ID") -protected Proc proc; - - - -
-
-
- Атрибуты типа enum - В стандартном варианте использования JPA для атрибутов типа enum в базе данных хранится целое число, получаемое методом ordinal() этого перечисления. Такой подход может привести к следующим проблемам при эксплуатации и развитии системы: - - при появлении в БД значения, не равного ни одному ordinal значению перечисления, экземпляр сущности нельзя загрузить вообще; - - - невозможно ввести новое значение между имеющимися, что актуально при использовании сортировки по значению перечисления (order by). - - - Чтобы решить эти проблемы, в подходе CUBA предлагается отвязать значение, хранимое в БД, от ordinal перечисления. Для этого необходимо поле класса сущности объявлять с типом, хранимым в БД (Integer или String), а методы доступа (getter / setter) создавать для типа самого перечисления. - Например:@Entity(name = "sales$Customer") -@Table(name = "SALES_CUSTOMER") -public class Customer extends StandardEntity { - - @Column(name = "GRADE") - protected Integer grade; - - public CustomerGrade getGrade() { - return grade == null ? null : CustomerGrade.fromId(grade); - } - - public void setGrade(CustomerGrade grade) { - this.grade = grade == null ? null : grade.getId(); - } -... -} - При этом сам класс перечисления может выглядеть следующим образом:public enum CustomerGrade implements EnumClass<Integer> { - - PREMIUM(10), - HIGH(20), - MEDIUM(30); - - private Integer id; - - CustomerGrade(Integer id) { - this.id = id; - } - - @Override - public Integer getId() { - return id; - } - - public static CustomerGrade fromId(Integer id) { - for (CustomerGrade grade : CustomerGrade.values()) { - if (grade.getId().equals(id)) - return grade; - } - return null; - } -} - Для правильного отражения в метаданных класс перечисления, используемый в качестве типа атрибута сущности, должен реализовывать интерфейс EnumClass. - Как видно из примеров, для атрибута grade в БД хранится значение типа Integer, задаваемое полем id перечисления CustomerGrade, а конкретно 10, 20 или 30. В то же время прикладной код и метаданные работают с самим типом CustomerGrade через методы доступа, которые и осуществляют конвертацию. - При наличии в поле БД значения, не соответствующего ни одному значению перечисления, метод getGrade() просто вернет null. Для ввода нового значения, например HIGHER, между HIGH и PREMIUM, достаточно добавить это значение в перечисление с идентификатором 15, при этом сортировка по полю Customer.grade останется верной. -
-
- Мягкое удаление - Платформа CUBA поддерживает режим "мягкого удаления" данных - когда вместо удаления записей из базы данных они только помечаются определенным образом и становятся недоступными для обычного использования. В дальнейшем такие записи можно либо совсем удалить из БД с помощью отдельной регламентной процедуры, либо восстановить. - Механизм мягкого удаления является "прозрачным" для прикладного программиста - достаточно убедиться что класс сущности реализует интерфейс SoftDelete, и платформа сама нужным образом будет модифицировать запросы и операции с данными. - Режим мягкого удаления имеет следующие преимущества: - - значительно снижается риск потери данных вследствие неверных действий пользователей - - - позволяет быстро сделать некоторые записи недоступными, даже если на них имеются ссылки. - Возьмем для примера модель данных Заказы - Покупатели. Допустим, на некоторого покупателя оформлено несколько заказов, однако нам нужно сделать его недоступным для дальнейшей работы. Традиционным "жестким" удалением сделать это невозможно, так как для удаления покупателя нам нужно либо удалить все его заказы, либо обнулить в этих заказах ссылки на него (т.е. потерять информацию). При мягком удалении покупателя он становится недоступным для поиска и изменения, однако при просмотре заказов пользователь видит на экране название покупателя, так как при загрузке связей признак удаления намеренно игнорируется. - Описанное поведение является стандартным, но может быть модифицировано с помощью политики обработки связей при удалении. - - - Отрицательной стороной мягкого удаления является увеличение объема базы данных и потенциальная необходимость дополнительных процедур ее очистки. -
- Использование - Для того, чтобы экземпляры сущности удалялись мягко, класс сущности должен реализовывать интерфейс SoftDelete, а соответствующая таблица БД должна содержать колонки: - - DELETE_TS - когда удалена запись - - - DELETED_BY - логин пользователя, который удалил запись - - - Поведение системы по умолчанию - сущности, реализующие SoftDelete, удаляются мягко, удаленные сущности не возвращаются запросами и поиском по идентификатору. При необходимости такое поведение можно динамически отключить следующими способами: - - для текущего экземпляра EntityManager - вызовом setSoftDeletion(false) - - - при запросе данных через - DataService - или - DataWorker - - вызовом у передаваемого объекта LoadContext метода setSoftDeletion(false) - - - на уровне источников данных - используя метод CollectionDatasource.setSoftDeletion(false) или атрибут softDeletion="false" элемента collectionDatasource в XML-дескрипторе экрана. - - - В режиме мягкого удаления платформа автоматически отфильтровывает удаленные экземпляры при загрузке по идентификатору и по JPQL-запросу, а также удаленные элементы связанных сущностей в атрибутах-коллекциях. Однако связанные сущности в единичных атрибутах загружаются независимо от того, удален связанный экземпляр или нет. -
-
- Политика обработки связей - Платформа предоставляет средство обработки связей при удалении сущностей, во многом аналогичное правилам ON DELETE внешних ключей в базе данных. Это средство работает на уровне Middleware и использует аннотации @OnDelete, @OnDeleteInverse атрибутов сущности. - Аннотация @OnDelete обрабатывается при удалении той сущности, в которой она встретилась, а не той, на которую указывает аннотированный атрибут (в этом отличие от каскадных удалений на уровне БД). - - -Аннотация @OnDeleteInverse обрабатывается при удалении той сущности, на которую указывает аннотированный атрибут, (т.е. аналогично каскадному удалению на уровне внешних ключей в БД). Эта аннотация полезна при отсутствии в удаляемом объекте атрибута, который нужно проверять при удалении. При этом, как правило, в проверяемом объекте существует ссылка на удаляемый, на этот атрибут и устанавливается аннотация @OnDeleteInverse. - Значением аннотации может быть: - - DeletePolicy.DENY - запретить удаление сущности, если аннотированный атрибут не null или не пустая коллекция - - - DeletePolicy.CASCADE - каскадно удалить аннотированный атрибут - - - DeletePolicy.UNLINK - разорвать связь с аннотированным атрибутом. Разрыв связи имеет смысл указывать только на ведущей стороне ассоциации - той, которая в классе сущности аннотирована @JoinColumn. - - - Примеры: - - Запрет удаления при наличии ссылки: при попытке удаления экземпляра Customer, на который ссылается хотя бы один Order, будет выброшено исключение DeletePolicyException. - Order.java@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "CUSTOMER_ID") -@OnDeleteInverse(DeletePolicy.DENY) -protected Customer customer; - Customer.java@OneToMany(mappedBy = "customer") -protected List<Order> orders; - - - Каскадное удаление элементов коллекции: при удалении экземпляра Role все экземпляры Permission также будут удалены. - Role.java@OneToMany(mappedBy = "role") -@OnDelete(DeletePolicy.CASCADE) -protected Set<Permission> permissions; - Permission.java@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "ROLE_ID") -protected Role role; - - - Разрыв связи с элементами коллекции: удаление экземпляра Role приведет к установке в null ссылок со стороны всех входивших в коллекцию экземпляров Permission. - Role.java@OneToMany(mappedBy = "role") -@OnDelete(DeletePolicy.UNLINK) -protected Set<Permission> permissions; - Permission.java@ManyToOne(fetch = FetchType.LAZY) -@JoinColumn(name = "ROLE_ID") -protected Role role; - - - Особенности реализации: - - Нужно быть осторожным при использовании @OnDeleteInverse с политиками CASCADE и UNLINK, так как при этом происходит извлечение из БД на сервер приложения всех экземпляров ссылающихся объектов, изменение и затем сохранение. - Например, в случае ассоциации Customer - Job и большого количества работ для одного заказчика, если поставить на атрибут Job.customer политику @OnDeleteInverse(CASCADE), то при удалении экземпляра заказчика будет предпринята попытка извлечь и изменить все его работы. Это может привести к перегрузке сервера приложения и БД. - С другой стороны, использование @OnDeleteInverse(DENY) безопасно, так как при этом производится только подсчет количества ссылающихся объектов, и если оно больше 0, выбрасывается исключение. Поэтому @OnDeleteInverse(DENY) для атрибута Job.customer вполне допустимо. - - - Политика обработки связей реализуется с помощью Entity Listeners, то есть при сохранении данных в БД на уровне Middleware. - - -
-
- Ограничение уникальности на уровне БД - В режиме мягкого удаления для ограничения уникальности некоторого значения необходимо обеспечить существование единственной неудаленной записи с этим значением, и произвольного количества удаленных записей с этим же значением. - Реализуется данная логика путем, специфичным для используемого сервера базы данных: - - Если сервер БД поддерживает частичные (partial) индексы (например PostgreSQL), то ограничение уникальности можно создать следующим образом:create unique index IDX_SEC_USER_UNIQ_LOGIN on SEC_USER (LOGIN_LC) where DELETE_TS is null - - - Если сервер БД не поддерживает частичные индексы (например Microsoft SQL Server 2005), то в уникальный индекс можно включить поле DELETE_TS:create unique index IDX_SEC_USER_UNIQ_LOGIN on SEC_USER (LOGIN_LC, DELETE_TS) - - -
-
-
- Entity Listeners - Entity Listeners предназначены для реакции на события жизненного цикла экземпляров сущностей на уровне Middleware. - Слушатель представляет собой класс, реализующий один или несколько интерфейсов пакета com.haulmont.cuba.core.listener. Слушатель будет реагировать на события типов, соответствующих реализуемым интерфейсам. - - - - BeforeDetachEntityListener - - - Метод onBeforeDetach() вызывается перед отделением объекта от - EntityManager - при коммите транзакции. - Данный слушатель можно использовать например для заполнения неперсистентных атрибутов сущности перед отправкой ее на клиентский уровень. - - - - - BeforeInsertEntityListener - - - Метод onBeforeInsert() вызывается перед выполнением вставки записи в БД. В данном методе возможны любые операции с текущим - EntityManager - . - - - - - AfterInsertEntityListener - - - Метод onAfterInsert() вызывается после выполнения вставки записи в БД, но до коммита транзакции. В данном методе нельзя модифицировать текущий персистентный контекст, однако можно производить изменения в БД с помощью - QueryRunner - . - - - - - BeforeUpdateEntityListener - - - Метод onBeforeUpdate() вызывается перед изменением записи в БД. В данном методе возможны любые операции с текущим - EntityManager - . - - - - - AfterUpdateEntityListener - - - Метод onAfterUpdate() вызывается после изменения записи в БД, но до коммита транзакции. В данном методе нельзя модифицировать текущий персистентный контекст, однако можно производить изменения в БД с помощью - QueryRunner - . - - - - - BeforeDeleteEntityListener - - - Метод onBeforeDelete() вызывается перед удалением записи из БД (или в случае мягкого удаления - перед изменением записи). В данном методе возможны любые операции с текущим - EntityManager - . - - - - - AfterDeleteEntityListener - - - Метод onAfterDelete() вызывается после удаления записи из БД (или в случае мягкого удаления - после изменения записи), но до коммита транзакции. В данном методе нельзя модифицировать текущий персистентный контекст, однако можно производить изменения в БД с помощью - QueryRunner - . - - - - Entity Listener может быть задан 2-мя способами: - - Статически - имена классов слушателей указываются в аннотации - @Listeners - на классе сущности - - - Динамически - класс сущности и слушателя передаются в метод addListener() бина EntityListenerManager, например: @ManagedBean -public class MyBean implements AppContext.Listener { - - @Inject - private EntityListenerManager entityListenerManager; - - public ClusterManager() { - AppContext.addListener(this); - } - - @Override - public void applicationStarted() { - entityListenerManager.addListener(User.class, MyUserListener.class); - } - - @Override - public void applicationStopped() { - } -} - - - Для всех экземпляров некоторого класса сущности создается и кэшируется один экземпляр слушателя определенного типа, поэтому слушатель не должен иметь состояния. - Если для сущности объявлены несколько слушателей одного типа (например аннотациями класса сущности и его предков, плюс динамически), то их вызов будет выполняться в следующем порядке: - - Для каждого предка начиная с самого дальнего вызываются его динамически добавленные слушатели, затем статически назначенные. - - - После всех предков вызываются динамически добавленные слушатели данного класса, затем статически назначенные. - - -
-
-
- Metadata Framework - Для эффективной работы с моделью данных в CUBA-приложениях используется фреймворк метаданных, который: - - - предоставляет удобный интерфейс для получения информации о сущностях, их атрибутах и отношениях между сущностями; а также для навигации по ссылкам - - - служит специализированной и более удобной в использовании альтернативой Java Reflection API - - - регламентирует допустимые типы данных и отношений между сущностями - - - позволяет создавать универсальные механизмы работы с данными - - -
- Интерфейсы метаданных - Рассмотрим основные интерфейсы метаданных. -
- Интерфейсы фреймворка метаданных - - - - - -
- - - - Session - - - Точка входа в фреймворк метаданных. Позволяет получать экземпляры MetaClass по имени и по соответствующему классу Java. Обратите внимание на различие методов getClass() и getClassNN() - первые могут возвращать null, вторые нет (NonNull). - Объект Session может быть получен через интерфейс инфраструктуры - Metadata - . - Пример:@Inject -protected Metadata metadata; -... -Session session = metadata.getSession(); -MetaClass metaClass1 = session.getClassNN("sec$User"); -MetaClass metaClass2 = session.getClassNN(User.class); -assert metaClass1 == metaClass2; - - - - - MetaModel - - - Редко используемый интерфейс, служит для группировки мета-классов. - Группировка осуществляется по первым трем частям имени пакета Java класса сущности. Например, мета-класс сущности com.abc.sales.entity.Customer принадлежит мета-модели с именем com.abc.sales - - - - - MetaClass - - - Интерфейс метаданных класса сущности. MetaClass всегда ассоциирован с классом Java, которого он представляет. - Основные методы: - - - getName() – имя сущности, по соглашению первой частью имени до знака $ является код пространства имен, например, sales$Customer - - - getProperties() – список мета-свойств (MetaProperty) - - - getProperty(), getPropertyNN() - получение мета-свойства по имени. Первый метод в случае отсутствия атрибута с указанным именем возвращает null, второй выбрасывает исключение. - Пример:MetaClass userClass = session.getClassNN(User.class); -MetaProperty groupProperty = userClass.getPropertyNN("group"); - - - getPropertyPath() - позволяет перемещаться по ссылкам. Данный метод принимает строковый параметр - путь из имен атрибутов, разделенных точкой. Возвращаемый объект MetaPropertyPath позволяет обратиться к искомому (последнему в пути) атрибуту вызовом getMetaProperty(). - Пример:MetaClass userClass = session.getClassNN(User.class); -MetaProperty groupNameProp = userClass.getPropertyPath("group.name").getMetaProperty(); -assert groupNameProp.getDomain().getName().equals("sec$Group"); - - - getJavaClass() – класс сущности, которому соответствует данный MetaClass - - - getAnnotations() – коллекция мета-аннотаций - - - - - - - MetaProperty - - - Интерфейс метаданных атрибута сущности. - Основные методы: - - - getName() – имя свойства, соответствует имени атрибута сущности - - - getDomain() – мета-класс, которому принадлежит данное свойство - - - getType() – тип свойства: - - простой тип: DATATYPE - - - перечисление: ENUM - - - ссылочный тип двух видов: - - - ASSOCIATION − простая ссылка на другую сущность. Например, отношение заказа и покупателя − ассоциация. - - - COMPOSITION − ссылка на сущность, которая не имеет самостоятельного значения без владеющей сущности. COMPOSITION можно считать "более тесным" отношением, чем ASSOCIATION. Например, отношение заказа и пункта этого заказа − COMPOSITION, т.к. пункт не может существовать без заказа, которому он принадлежит. - - - Вид ссылочного атрибута ASSOCIATION или COMPOSITION влияет на режим редактирования сущности: в первом случае сохранение связанной сущности в базу данных происходит независимо, а во втором − связанная сущность сохраняется в БД только вместе с владеющей сущностью. - - - - - getRange() – интерфейс Range, детально описывающий тип данного атрибута - - - isMandatory() – признак обязательности атрибута. Используется, например, визуальными компонентами для сигнализации пользователю о необходимости ввода значения. - - - isReadOnly() – признак неизменности атрибута - - - getInverse() – для ссылочного атрибута возвращает мета-свойство с обратной стороны ассоциации, если таковое имеется - - - getAnnotatedElement() – поле (java.lang.reflect.Field) или метод (java.lang.reflect.Method), соответствующие данному атрибуту сущности - - - getJavaType() – класс Java данного атрибута сущности. Это либо тип поля класса, либо тип возвращаемого значения метода. - - - getDeclaringClass() – класс Java, содержащий данный атрибут - - - - - - - Range - - - Интерфейс, детально описывающий тип атрибута сущности. - Основные методы: - - - isDatatype() – возвращает true для атрибута простого типа - - - asDatatype() - возвращает - Datatype - для атрибута простого типа - - - isEnum() – возвращает true для атрибута типа перечисления - - - asEnumeration() - возвращает - Enumeration - для атрибута типа перечисления - - - isClass() – возвращает true для ссылочного атрибута типа ASSOCIATION или COMPOSITION - - - asClass() - возвращает мета-класс ассоциированной сущности для ссылочного атрибута - - - isOrdered() – возвращает true если атрибут представляет собой упорядоченную коллекцию (например List) - - - getCardinality() – вид отношения для ссылочного атрибута: ONE_TO_ONE, MANY_TO_ONE, ONE_TO_MANY, MANY_TO_MANY - - - - - -
-
- Формирование метаданных - Основной источник формирования структуры метаданных - аннотированные классы сущностей. - Класс сущности отражается в метаданные в следующих случаях: - - Класс персистентной сущности аннотирован @Entity, @Embeddable, @MappedSuperclass и зарегистрирован в файле - persistence.xml - - - - Класс неперсистентной сущности аннотирован @MetaClass и зарегистрирован в файле - metadata.xml - - - - Атрибут сущности отражается в метаданных, если: - - поле класса аннотировано @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded - - - поле класса или метод доступа на чтение (getter) аннотирован @MetaProperty - - - Параметры мета-класса и мета-свойств формируются на основе параметров перечисленных аннотаций, а также типов полей и методов класса. Кроме того, если у атрибута отсутствует метод доступа на запись (setter), атрибут становится неизменяемым (read only). - - Внимание - Текущая реализация сборки метаданных имеет следующие ограничения: - - Классы сущностей должны размещаться в пакетах, имеющих не менее 3-х уровней иерархии, например com.abc.sales, com.abc.sales.entity - - - Персистентные сущности не могут ссылаться на неперсистентные. Обратное возможно, т.е. неперсистентная сущность может иметь атрибут типа некоторой персистентной сущности. - - - -
-
- Datatype - Интерфейс Datatype описывает тип данных, допустимый для атрибута сущности, не являющегося ассоциацией. Каждый экземпляр реализации Datatype соответствует одному классу Java, для работы с которым он предназначен. - Все экземпляры зарегистрированы в репозитории - классе Datatypes, который выполняет загрузку и инициализацию классов реализации Datatype следующим образом: - - в корне CLASSPATH ищется файл - datatypes.xml - , и если он найден, репозиторий Datatypes инициализируется из него - - - в противном случае инициализация Datatypes производится из файла /com/haulmont/chile/core/datatypes/datatypes.xml - - - Экземпляр Datatype может быть получен двумя способами: - - из мета-свойства типа - DATATYPE - , вызовом getRange().asDatatype() - - - статическим методом Datatypes.get(), передавая в него имя реализации Datatype или класс Java, для которого он создан. - - - Основные методы интерфейса Datatype: - - getName() - возвращает уникальное имя данной реализации - - - format() - преобразовывает переданное значение в строку - - - parse() - преобразовывает строку в значение нужного типа - - - Datatype определяет два набора методов для форматирования/парсинга: с учетом локали и без учета локали. Преобразование с учетом локали используется повсеместно в пользовательском интерфейсе, преобразование без учета локали используется в системных механизмах, например для сериализации в REST API. - Форматы для преобразований без учета локали задаются в вышеупомянутом файле - datatypes.xml - . - Форматы для преобразований с учетом локали задаются в главных пакетах локализованных сообщений, в строках со следующими ключами: - - numberDecimalSeparator - - - numberGroupingSeparator - - - integerFormat - - - doubleFormat - - - dateTimeFormat - - - dateFormat - - - timeFormat - - - trueString - - - falseString - - Все перечисленные форматы по умолчанию заданы в главных пакетах локализованных сообщений базового проекта cuba, и могут быть переопределены в аналогичных файлах проекта приложения. -
- Пример форматирования даты в UI - Рассмотрим отображение атрибута Order.date в таблице браузера заказов. - order-browse.xml<table id="ordersTable"> - ... - <columns> - <column id="date"/> - ... - Атрибут date в классе Order определен с типом "дата":@Column(name = "DATE", nullable = false) -@Temporal(TemporalType.DATE) -private Date date; - Если текущий пользователь зарегистрирован c русской локалью, то из главного пакета локализованных сообщений клиентского уровня, из файла messages_ru.properties извлекается строка:dateFormat=dd.MM.yyyy - Результат: дата "6 августа 2012 года" конвертируется в строку "06.08.2012" для отображения в ячейке таблицы. -
-
- Примеры форматирования дат и чисел в коде приложения - - - Пример форматирования даты@Inject -protected UserSessionSource userSessionSource; -... -Date date = ...; -String dateStr = Datatypes.get(Date.class).format(date, userSessionSource.getLocale()); - - - Пример форматирования числового значения с повышенной точностью (5 знаков после запятой) в блоке Web Client - /com/abc/sales/web/messages_ru.propertiescoordinateFormat = #,##0.00000 - SomeClass.java@Inject -protected Messages messages; -@Inject -protected UserSessionSource userSessionSource; -... -String coordinateFormat = messages.getMainMessage("coordinateFormat"); -FormatStrings formatStrings = Datatypes.getFormatStrings(userSessionSource.getLocale()); -NumberFormat format = new DecimalFormat(coordinateFormat, formatStrings.getFormatSymbols()); - -String formattedValue = format.format(value); - - -
-
-
- Мета-аннотации - Мета-аннотации сущностей - набор пар ключ/значение, содержащих дополнительную информацию о сущностей. - Обращение к мета-аннотациям производится с помощью метода мета-класса getAnnotations(). - Источниками мета-аннотаций сущности являются: - - Аннотации @OnDelete, @OnDeleteInverse, @Extends. При этом в мета-аннотациях создаются служебные объекты связей между сущностями. - - - Аннотации @SystemLevel, @EnableRestore, @TrackEditScreenHistory. При этом создаются мета-аннотации с ключами, соответствующими полному имени класса Java аннотации. - - - Опционально: в прикладном проекте могут быть определены свои аннотации для сущностей, и в переопределенном методе MetadataImpl.initMetaAnnotations() отображены в соответствующие мета-аннотации. - - - Опционально: в файлах - metadata.xml - также могут быть определены мета-аннотации сущностей. Если мета-аннотация в XML имеет то же имя, что и мета-аннотация, созданная по Java аннотации класса сущности, первая переопределит значение второй. - Пример определения мета-аннотаций в - metadata.xml - :<annotations> - <entity class="com.haulmont.cuba.security.entity.User"> - <annotation name="com.haulmont.cuba.core.entity.annotation.TrackEditScreenHistory" value="false"/> - <annotation name="com.haulmont.cuba.core.entity.annotation.EnableRestore" value="true"/> - </entity> -</annotations> - - -
-
-
- Представления -
- Общие сведения - При извлечении сущностей из базы данных обычно встает вопрос - как обеспечить загрузку связанных сущностей на нужную глубину? - Например, для браузера Заказов нужно отобразить дату и сумму заказа совместно с названием Покупателя, т.е. загрузить связанный экземпляр Покупателя. А для экрана редактирования Заказа необходимо загрузить еще и коллекцию Пунктов заказа, причем каждый Пункт заказа должен содержать связанный экземпляр Товара для отображения его наименования. - Загрузка по требованию в большинстве случаев не может помочь, так как обработка данных как правило происходит не в транзакции, в которой загружаются сущности, а, например, на клиентском уровне в пользовательском интерфейсе. В то же время задание энергичной загрузки в аннотациях сущностей недопустимо, так как приводит к постоянному извлечению всего графа связанных сущностей, который может быть очень большим. - Другой похожей проблемой является ограничение набора локальных атрибутов сущностей загружаемого графа: например, некоторая сущность имеет 50 атрибутов, в том числе BLOB, а в экране отображается только 10 атрибутов. Зачем загружать из БД, затем сериализовать и передавать клиенту 40 атрибутов, которые ему в данный момент не нужны? - Механизм представлений (views) решает эти проблемы, обеспечивая извлечение из базы данных и передачу клиенту графов сущностей, ограниченных в глубину и по атрибутам. Представление является описателем графа объектов, который требуется в некотором экране UI или другом процессе обработки данных. - Обработка представлений производится следующим образом: - - все связи в модели данных объявляются с признаком загрузки по требованию (fetch = FetchType.LAZY, см. ) - - - в процессе загрузки данных через DataService клиентский код помимо JPQL запроса указывает нужное представление - - - на основе представления формируется так называемый Fetch Plan - особенность лежащего в основе слоя ORM фреймворка Apache OpenJPA. Fetch Plan влияет на формирование SQL запроса к базе данных: как на список возвращаемых полей, так и на соединения с другими таблицами, содержащими связанные сущности. - - - ссылки, не включенные в Fetch Plan (иногда это полезно для упрощения основного SQL запроса), загружаются отдельными SQL запросами, для чего механизм обработки представлений просто обращается к соответствующим методам чтения атрибутов - - - в результате на момент завершения транзакции, загружающей данные, в памяти Middleware содержится граф объектов, заданный JPQL запросом и представлением. - - -
- Классы представления - - - - - -
- Представление определяется экземпляром класса View, в котором: - - entityClass - класс сущности, для которого определено представление. Другими словами, "корень" дерева загружаемых сущностей. - - - name - имя представления. Должно быть либо null, либо уникальным в пределах данной сущности. - - - properties - коллекция экземпляров класса ViewProperty, соответствующих загружаемым атрибутам сущности. - - - includeSystemProperties - признак включения системных атрибутов (входящих в состав базовых интерфейсов персистентных сущностей BaseEntity и Updatable). Системные атрибуты не перечисляются в properties явно, а учитываются механизмом обработки представлений в зависимости от того, какие интерфейсы реализует данная сущность. - - - Класс ViewProperty имеет следующие свойства: - - name - имя атрибута сущности - - - view - для ссылочных атрибутов задает представление, с которым необходимо загружать связанную сущность - - - lazy - для ссылочных атрибутов признак того, что данный атрибут нужно не включать в Fetch Plan, а загружать отдельным SQL запросом, инициированным обращением к атрибуту. Следует иметь в виду, что атрибут в любом случае будет загружен на момент окончания транзакции, данный признак влияет только на способ загрузки. - - - - Независимо от набора атрибутов, определенного в представлении, всегда загружаются следующие атрибуты: - - id - идентификатор сущности - - - version - для оптимистично блокируемых сущностей, реализующих Versioned - - - deleteTs, deletedBy - для сущностей, реализующих - SoftDelete - - - - -
-
- Создание представлений - Представление может быть создано двумя путями: - - программно - созданием экземпляра View, например:View view = new View(Order.class) - .addProperty("date") - .addProperty("amount") - .addProperty("customer", new View(Customer.class) - .addProperty("name") - ); - Как правило, таким способом создаются представления, используемые только в каком-то одном месте бизнес-логики. - - Механизм обработки представлений кэширует создаваемые для именованных представлений объекты Fetch Plan. Это означает, что если Вы программно создадите два представления для одной сущности с одинаковыми именами, но с разным набором атрибутов, то выборка со вторым представлением окажется неверной. - Поэтому никогда не задавайте имен для "одноразовых" представлений, создаваемых программно. - - - - декларативно - путем создания описателя на XML и его развертывания в репозитории представлений ViewRepository. При развертывании на основе XML-описателя создаются и кэшируются экземпляры View. В дальнейшем в любом месте кода приложения требуемое представление можно получить вызовом репозитория с указанием класса сущности и имени представления. - - - Рассмотрим подробнее декларативный способ создания и работы с представлениями. - Ссылка на ViewRepository может быть получена через интерфейс инфраструктуры - Metadata - . Для получения экземпляра View, содержащегося в репозитории, используются методы getView(). Для развертывания XML-описателей представлений в репозитории используются методы deployViews(). - В репозитории для каждой сущности по умолчанию доступны два представления с именами _local и _minimal: - - _local включает в себя все локальные атрибуты сущности - - - _minimal включает в себя атрибуты, входящие в имя экземпляра сущности, и задаваемые аннотацией - @NamePattern - . Если аннотация @NamePattern для сущности не указана, данное представление не включает никаких атрибутов. - - - Подробная структура XML-описателей изложена в - Пример описателя представления для сущности Заказ, которое должно обеспечить загрузку всех локальных атрибутов, ассоциированного Покупателя и коллекции Пунктов заказа:<view class="com.sample.sales.entity.Order" - name="orderWithCustomer" - extends="_local"> - <property name="customer" view="_minimal"/> - <property name="items" view="itemsInOrder"/> -</view> - Рекомендуемый способ группировки и развертывания описателей представлений: - - В модуле core в корне src создать файл {имя_проекта}-views.xml и поместить в него все описатели представлений, которые должны быть доступны глобально, т.е. на всех уровнях приложения. - - - Зарегистрировать данный файл в свойстве - cuba.viewsConfig - блока Middleware, т.е. в файле {имя_проекта}-app.properties модуля core. Это обеспечит автоматическое развертывание представлений на старте приложения в репозитории Middleware (см. метод MetadataImpl.initViews()). Развернутые представления будут доступны и для Middleware, и для клиентских блоков, которые получат соответствующие экземпляры View при подключении к среднему слою. - - - Если существуют представления, которые необходимы только какому-то одному клиентскому блоку приложения, то можно определить их в аналогичном файле этого блока, например {имя_проекта}-web-views.xml, и зарегистрировать в свойстве - cuba.viewsConfig - этого блока, т.е. в данном случае в файле {имя_проекта}-web-app.properties. Тогда клиентский блок сначала загрузит все имеющиеся представления с Middleware, а затем развернет свои собственные (см. MetadataClientImpl.initViews()). - Возможна также ситуация, когда некоторому клиентскому блоку не нужны глобальные представления, зарегистрированные на среднем слое, или нужны редко. В этом случае имеет смысл объявить в этом клиентском блоке свойство - cuba.lazyLoadServerViews - , тогда глобальные представления будут загружаться на клиентский уровень не все сразу, а по необходимости. - - - Если на момент развертывания некоторого представления в репозитории уже есть представление для этого же класса сущности и с таким же именем, то новое будет проигнорировано. Для того, чтобы представление заменило имеющееся в репозитории и гарантированно было развернуто, в XML-описателе должен быть явно указан атрибут overwrite = "true". В частности, развертывание представлений клиентского уровня производится после загрузки представлений со среднего слоя, поэтому некоторое клиентское представление может оказаться "замаскированным" одноименным представлением Middleware. - - Рекомендуется давать представлениям "описательные" имена. Например, не "browse", а "customerBrowse". Это упрощает поиск XML-описателей представлений по имени в процессе разработки приложения. - -
-
-
- Интерфейсы инфраструктуры - Интерфейсы инфраструктуры обеспечивают доступ к часто используемой функциональности платформы. Большинство из этих интерфейсов расположены в модуле global и могут быть использованы как на среднем слое, так и в блоках клиентского уровня, но некоторые доступны только коду среднего слоя. - Интерфейсы инфраструктуры реализуются бинами Spring Framework, поэтому они могут быть инжектированы в любые другие управляемые компоненты (Managed Beans, сервисы среднего слоя, контроллеры экранов универсального пользовательского интерфейса. - Кроме того, как и любые другие бины, интерфейсы инфраструктуры могут быть получены с помощью статических методов класса AppBeans, и использоваться в неуправляемых компонентах (POJO, вспомогательных классах и пр.). -
- Configuration - Позволяет получать ссылки на конфигурационные интерфейсы. - Примеры:@Inject -protected Configuration configuration; -... -String tempDir = configuration.getConfig(GlobalConfig.class).getTempDir();protected GlobalConfig globalConfig; - -@Inject -public void setConfiguration(Configuration configuration) { - this.globalConfig = configuration.getConfig(GlobalConfig.class); -}String tempDir = AppBeans.get(Configuration.class).getConfig(GlobalConfig.class).getTempDir(); -
-
- Messages - Интерфейс Messages обеспечивает получение локализованных строк сообщений. - Рассмотрим методы интерфейса подробнее. - - - getMessage() - возвращает локализованное сообщение по ключу, имени пакета сообщений и требуемой локали. Существует несколько модификаций данного метода в зависимости от набора параметров. Если локаль не указана в параметре метода, используется локаль текущего пользователя. - Примеры:@Inject -protected Messages messages; -... -String message1 = messages.getMessage(getClass(), "someMessage"); -String message2 = messages.getMessage("com.abc.sales.web.customer", "someMessage"); -String message3 = messages.getMessage(RoleType.STANDARD); - - - formatMessage() - находит локализованное сообщение по ключу, имени пакета сообщений и требуемой локали, и использует его для форматирования переданных параметров. Формат задается по правилам метода String.format(). Существует несколько модификаций данного метода в зависимости от набора параметров. Если локаль не указана в параметре метода, используется локаль текущего пользователя. - Пример:String formattedValue = messages.formatMessage(getClass(), "someFormat", someValue); - - - getMainMessage() - возвращает локализованное сообщение из главного пакета данного блока приложения. - Пример:protected Messages messages = AppBeans.get(Messages.class); -... -messages.getMainMessage("actions.Ok"); - - - getMainMessagePack() - возвращает имя главного пакета сообщений данного блока приложения. - Пример:String formattedValue = messages.formatMessage(messages.getMainMessagePack(), "someFormat", someValue); - - - getTools() - возвращает экземпляр интерфейса MessageTools (см. ниже). - - -
- MessageTools - ManagedBean, содержащий вспомогательные методы работы с локализованными сообщениями. Интерфейс MessageTools можно получить либо методом Messages.getTools(), либо как любой другой бин - инжекцией или через класс AppBeans. - Методы MessageTools: - - loadString() - возвращает локализованное сообщение, заданное ссылкой вида msg://{messagePack}/{key} - Составные части ссылки: - - msg:// - обязательный префикс - - - {messagePack} - необязательное имя пакета сообщения. Если не указано, предполагается, что имя пакета передается в loadString() отдельным параметром - - - {key} - ключ сообщения в пакете - - - Примеры ссылок на сообщения:msg://someMessage -msg://com.abc.sales.web.customer/someMessage - - - getEntityCaption() - возвращает локализованное название сущности - - - getPropertyCaption() - возвращает локализованное название атрибута сущности - - - hasPropertyCaption() - определяет, задано ли для атрибута сущности локализованное название - - - getLocValue() - возвращает локализованное значение атрибута сущности, основываясь на определении аннотации - @LocalizedValue - - - - getMessageRef() - формирует для мета-свойства ссылку на сообщение, по которой можно получить локализованное название атрибута сущности - - - Для расширения набора вспомогательных методов в конкретном приложении бин MessageTools можно переопределить. Примеры работы с расширенным интерфейсом:MyMessageTools tools = messages.getTools(); -tools.foo();((MyMessageTools) messages.getTools()).foo(); -
-
-
- Metadata - Интерфейс Metadata обеспечивает доступ к сессии метаданных и репозиторию представлений. - Методы интерфейса: - - getSession() - возвращает экземпляр сессии метаданных - - - getViewRepository() - возвращает экземпляр репозитория представлений - - - getExtendedEntities() - возвращает экземпляр ExtendedEntities, предназначенный для работы с расширенными сущностями. Подробнее см. - - - create() - создать экземпляр сущности, учитывая возможность расширения. Подробнее см. - - - getTools() - возвращает экземпляр интерфейса MetadataTools (см. ниже). - - -
- MetadataTools - ManagedBean, содержащий вспомогательные методы работы с метаданными. Интерфейс MetadataTools можно получить либо методом Metadata.getTools(), либо как любой другой бин - инжекцией или через класс AppBeans. - Методы MetadataTools: - - getAllPersistentMetaClasses() - возвращает коллекцию мета-классов персистентных сущностей - - - getAllEmbeddableMetaClasses() - возвращает коллекцию мета-классов встраиваемых сущностей - - - getAllEnums() - возвращает коллекцию классов перечислений, используемых в качестве типов атрибутов сущностей - - - format() - форматирует переданное значение в соответствии с типом данных заданного мета-свойства - - - isSystem() - определяет, является ли переданное мета-свойство системным, т.е. заданным в одном из базовых интерфейсов сущностей - - - isPersistent() - определяет, является ли переданное мета-свойство персистентным, т.е. хранимым в БД - - - isTransient() - определяет, является ли переданное мета-свойство или произвольный атрибут неперсистентным - - - isEmbedded() - определяет, является ли переданное мета-свойство встроенным объектом - - - isAnnotationPresent() - определяет наличие указанной аннотации на классе или его предках - - - getNamePatternProperties() - возвращает коллекцию мета-свойств атрибутов, входящих в имя экземпляра, возвращаемого методом Instance.getInstanceName(). См. - @NamePattern - . - - - Для расширения набора вспомогательных методов в конкретном приложении бин MetadataTools можно переопределить. Примеры работы с расширенным интерфейсом:MyMetadataTools tools = metadata.getTools(); -tools.foo();((MyMetadataTools) metadata.getTools()).foo(); -
-
-
- Persistence - Интерфейс уровня Middleware, являющийся точкой входа в функциональность хранения данных в БД. - Интерфейс Persistence доступен только в модуле core проекта приложения. - - Методы интерфейса: - - createTransaction(), getTransaction() - получить интерфейс управления транзакциями - - - isInTransaction() - определяет, существует ли в данный момент активная транзакция - - - getEntityManager() - возвращает экземпляр - EntityManager - для текущей транзакции - - - isSoftDeletion() - позволяет определить, активен ли режим мягкого удаления - - - setSoftDeletion() - устанавливает или отключает режим мягкого удаления. Влияет на аналогичный признак всех создаваемых экземпляров EntityManager. По умолчанию мягкое удаление включено. - - - getDbDialect() - возвращает диалект используемой в данный момент базы данных. Интерфейс DbDialect определяет тип БД и некоторые специфические для данной БД параметры. - В прикладном коде данный метод можно использовать для определения типа используемой БД, например:@Inject -protected Persistence persistence; -... -if (persistence.getDbDialect() instanceof PostgresDbDialect) -... - - - getDataSource() - получить javax.sql.DataSource для используемой в данный момент базы данных. - - Для всех объектов javax.sql.Connection, получаемых методом getDataSource().getConnection(), необходимо после использования соединения вызвать метод close() в секции finally. В противном случае соединение не вернется в пул, через какое-то время пул переполнится, и приложение не сможет выполнять запросы к базе данных. - - - - getTools() - возвращает экземпляр интерфейса PersistenceTools (см. ниже). - - -
- PersistenceTools - ManagedBean, содержащий вспомогательные методы работы с хранилищем данных. Интерфейс PersistenceTools можно получить либо методом Persistence.getTools(), либо как любой другой бин - инжекцией или через класс AppBeans. - Методы PersistenceTools: - - getDirtyFields() - возвращает коллекцию имен атрибутов сущности, измененных со времени последней загрузки экземпляра из БД. Для новых экземпляров возвращает пустую коллекцию. - - - isLoaded() - определяет, загружен ли из БД указанный атрибут экземпляра. Атрибут может быть не загружен, если он не указан в примененном при загрузке представлении. - Данный метод работает только для экземпляров в состоянии Managed. - - - getReferenceId() - возвращает идентификатор связанной сущности без загрузки ее из БД. - Предположим, в персистентный контекст загружен экземпляр Order, и нужно получить значение идентификатора экземпляра Customer, связанного с данным Заказом. Стандартное решение order.getCustomer().getId() приведет к выполнению SQL запроса к БД для загрузки экземпляра Customer, что в данном случае избыточно, так как значение идентификатора Покупателя физически находится также и в таблице Заказов. Выполнение же persistence.getTools().getReferenceId(order, "customer")не вызывет никаких дополнительных запросов к базе данных. - Данный метод работает только для экземпляров в состоянии Managed. - - - reloadEntity() - перезагрузить экземпляр сущности с указанным представлением. Данный метод должен вызываться внутри активной транзакции. - - Для расширения набора вспомогательных методов в конкретном приложении бин PersistenceTools можно переопределить. Примеры работы с расширенным интерфейсом:MyPersistenceTools tools = persistence.getTools(); -tools.foo();((MyPersistenceTools) persistence.getTools()).foo(); -
-
- PersistenceHelper - Вспомогательный класс для получения информации о персистентных сущностях. В отличие от бинов Persistence и PersistenceTools доступен на всех уровнях приложения. - Методы PersistenceHelper: - - isNew() - определяет, является ли переданный экземпляр только что созданным, т.е. находящимся в состоянии New. Возвращает true также если экземпляр не является персистентной сущностью. - - - isDetached() - определяет, находится ли переданный экземпляр в состоянии Detached. Возвращает true также если экземпляр не является персистентной сущностью. - - - isSoftDeleted() - определяет, поддерживает ли переданный класс сущности мягкое удаление - - - getEntityName() - возвращает имя сущности, заданное в аннотации @Entity - - - getTableName() - возвращает имя таблицы БД, хранящей экземпляры сущности, заданное в аннотации @Table - - -
-
-
- Resources - Обеспечивает загрузку ресурсов по следующим правилам: - - если указанное местонахождение представляет собой URL, ресурс загружается из этого URL; - - - если указанное местонахождение начинается с префикса classpath:, ресурс загружается из classpath; - - - если не URL и не начинается с classpath:, то: - - в каталоге конфигурации приложения ищется файл, используя указанное местонахождение как относительный путь. Если файл найден, ресурс загружается из него; - - - если ресурс не найден на предыдущих этапах, он загружается из classpath. - - - - - На практике явное указание URL или префикса classpath: используется редко, т.е. обычно ресурсы загружаются либо из конфигурационного каталога, либо из classpath. Ресурс в конфигурационном каталоге замещает одноименный ресурс в classpath. - Методы Resources: - - getResourceAsStream() - возвращает InputStream для указанного ресурса, либо null, если ресурс не найден. Поток должен быть закрыт после использования, например:@Inject -protected Resources resources; -... -InputStream stream = null; -try { - stream = resources.getResourceAsStream(resourceLocation); - ... -} finally { - IOUtils.closeQuietly(stream); -} - Возможно использование "try with resources":try (InputStream stream = resources.getResourceAsStream(resourceLocation)) { - ... -} - - - getResourceAsString() - возвращает указанный ресурс в виде строки, либо null, если ресурс не найден - - -
-
- Scripting - Интерфейс Scripting позволяет динамически (т.е. во время работы приложения) компилировать и загружать классы Java и Groovy, а также выполнять скрипты и выражения на Groovy. - Методы Scripting: - - evaluateGroovy() - выполняет выражение на Groovy и возвращает его результат. - Свойство приложения - cuba.groovyEvaluatorImport - позволяет определить общий набор импортируемых классов, подставляемых в каждое выполняемое выражение. По умолчанию все стандартные блоки приложения импортируют класс - PersistenceHelper - . - Скомпилированные выражения кэшируются, что значительно ускоряет повторное выполнение. - Пример:@Inject -protected Scripting scripting; -... -Integer intResult = scripting.evaluateGroovy("2 + 2", new Binding()); - -Binding binding = new Binding(); -binding.setVariable("instance", new User()); -Boolean boolResult = scripting.evaluateGroovy("return PersistenceHelper.isNew(instance)", binding); - - - runGroovyScript() - выполняет скрипт Groovy и возвращает его результат. - Скрипт должен быть расположен либо в конфигурационном каталоге приложения, либо в classpath (текущая реализация Scripting поддерживает ресурсы classpath только внутри JAR-файлов). Скрипт в конфигурационном каталоге замещает одноименный скрипт в classpath. - Путь к скрипту указывается с разделителями /, в начале пути символ / не требуется. - Пример:@Inject -protected Scripting scripting; -... -Binding binding = new Binding(); -binding.setVariable("itemId", itemId); -BigDecimal amount = scripting.runGroovyScript("com/abc/sales/CalculatePrice.groovy", binding); - - - loadClass() - загружает Java или Groovy класс, используя следующую последовательность действий: - - Если класс уже загружен, возвращает его. - - - Ищет исходный текст Groovy (файл *.groovy) в каталоге конфигурации. Если найден, компилирует его, загружает и возвращает класс. - - - Ищет исходный текст Java (файл *.java) в каталоге конфигурации. Если найден, компилирует его, загружает и возвращает класс. - - - Ищет скомпилированный класс в classpath, если найден - загружает и возвращает его. - - - Если ничего не найдено, возвращает null. - - - Файлы исходных текстов Java и Groovy в каталоге конфигурации можно изменять во время работы приложения. При следующем вызове loadClass() соответствующий класс будет перекомпилирован и возвращен новый, однако существуют следующие ограничения: - - нельзя изменять тип исходного текста с Groovy на Java - - - если существовал исходный текст Groovy, и был однажды скомпилирован, то удаление файла исходного текста не приведет к загрузке другого класса из classpath - будет по прежнему возвращаться класс, скомпилированный из удаленного исходника. - - - Пример:@Inject -protected Scripting scripting; -... -Class calculatorClass = scripting.loadClass("com.abc.sales.PriceCalculator"); - - - getClassLoader() - возвращает ClassLoader, способный работать по правилам, описанным выше для метода loadClass(). - - - Кэш скомпилированных классов можно очистить во время выполнения с помощью JMX-бинов CachingMBean и CachingFacadeMBean. - См. также -
-
- Security - Обеспечивает авторизацию - проверку прав пользователя на различные объекты системы. Большинство методов просто получают текущую пользовательскую сессию через UserSessionSource и делегируют ей выполнение авторизации. Подробнее см. -
-
- TimeSource - Обеспечивает получение текущего времени. Применение new Date() и т.п. в прикладном коде не рекомендуется. - Примеры:@Inject -protected TimeSource timeSource; -... -Date date = timeSource.currentTimestamp();long startTime = AppBeans.get(TimeSource.class).currentTimeMillis(); -
-
- UserSessionSource - Обеспечивает получение объекта сессии текущего пользователя. Подробнее см. -
-
- UuidSource - Обеспечивает получение значений UUID, в том числе для идентификаторов сущностей. Применение UUID.randomUUID() в прикладном коде не рекомендуется. -
-
-
- AppContext - AppContext - системный класс, в статических полях которого хранятся ссылки на некоторые общие для любого блока приложения компоненты: - - ApplicationContext фреймворка Spring - - - Набор свойств приложения, загруженных из файлов app.properties - - - ThreadLocal переменная, хранящая экземпляры SecurityContext - - - Коллекция слушателей жизненного цикла приложения ( AppContext.Listener) - - - AppContext инициализируется на запуске приложения классами-загрузчиками, специфичными для типа блока приложения: - - загрузчик Middleware - AppContextLoader - - - загрузчик Web Client - WebAppContextLoader - - - загрузчик Web Portal - PortalAppContextLoader - - - загрузчик Desktop Client - DesktopAppContextLoader - - - AppContext может быть использован в прикладном коде для решения следующих задач: - - Регистрации слушателей, срабатывающих после полной инициализации и перед закрытием приложения, например:AppContext.addListener(new AppContext.Listener() { - @Override - public void applicationStarted() { - System.out.println("Application is ready"); - } - - @Override - public void applicationStopped() { - System.out.println("Application is closing"); - } -}); - - - Проверки того, что данный блок приложения полностью инициализирован и готов к выполнению. Такая проверка обычно необходима в методах бинов, автоматически запускаемых планировщиком Sping Framework. - Пример:if (!AppContext.isStarted()) - return; - - - Получения значений свойств приложения, хранимых в файлах app.properties, если они недоступны через конфигурационные интерфейсы. - - - Передачи SecurityContext в новые потоки выполнения, см. . - - - - Для получения ссылок на Spring-бины используйте инжекцию или статические методы класса AppBeans. - Использование AppContext.getApplicationContext().getBean() не рекомендуется. - -
-
- Свойства приложения -
- Основные понятия - Свойства приложения − именованные данные различных типов, определяющие всевозможные аспекты конфигурации и функционирования приложения. - По назначению свойства приложения можно классифицировать следующим образом: - - - Конфигурационные параметры - задают наборы конфигурационных файлов и некоторые параметры пользовательского интерфейса, т.е. определяют функциональность приложения. - Например: cuba.springContextConfig, cuba.web.useLightHeader. - - - Параметры развертывания - различные URL для соединения блоков приложения, тип используемой БД, настройки подсистемы безопасности и т.д. - Например: cuba.connectionUrl, cuba.dbmsType, - cuba.userSessionExpirationTimeoutSec - . - - - Параметры времени выполнения - активность аудита, параметры отсылки email и т.д. - Например: cuba.security.EntityLog.enabled, cuba.email.smtpHost. - - - Как правило, некоторое свойство принадлежит только одному или нескольким блокам приложения. Например, - cuba.persistenceConfig - имеет смысл только для Middleware, cuba.web.useLightHeader − только для Web Client, а - cuba.springContextConfig - − для всех блоков. - Принадлежность к блоку означает, что если нужно задать значение некоторому свойству, это необходимо сделать во всех блоках, которым данное свойство принадлежит (и которые используются в приложении). - Принадлежность можно выяснить следующими способами: - - Из документации: см. - - - Проследив использование свойства в коде приложения - - - Если к свойству есть доступ через конфигурационный интерфейс, то по принадлежности интерфейса модулю проекта. - - -
-
- Доступ к свойствам - Основной способ доступа к свойствам приложения из прикладного кода − использование механизма конфигурационных интерфейсов. Кроме того, все параметры конфигурации и развертывания доступны через методы класса - AppContext - . - Некоторые блоки приложения определяют JMX-интерфейсы для доступа к свойствам приложения. В частности, на уровне Middleware имеется JMX-интерфейс - ConfigStorageMBean - , а на уровне Web Client - ConfigurationMBean - . Методы этих интерфейсов работают по отдельности с параметрами конфигурации и развертывания (*AppProperties) и с параметрами времени выполнения (*DbProperties). Это обусловлено различием механизмов хранения этих категорий свойств. -
-
- Хранение свойств в файлах - Свойства, определяющие конфигурацию и параметры развертывания, задаются в специальных файлах свойств, имеющих имя вида *-app.properties. Каждый блок приложения имеет набор таких файлов, включающий в себя файлы из базовых проектов платформы и файл текущего приложения. Набор файлов свойств определяется следующим образом: - - - Для блоков, являющихся веб-приложениями (Middleware, Web Client, Web Portal) набор файлов свойств задается в web.xml в параметре appPropertiesConfig. - - - Для блока Desktop Client основной способ задания набора файлов свойств − переопределение в приложении метода getDefaultAppPropertiesConfig() в классе-наследнике com.haulmont.cuba.desktop.App. - - - Например, набор файлов свойств блока Middleware проекта sales задается в файле web/WEB-INF/web.xml модуля core, и выглядит следующим образом: - classpath:cuba-app.properties -classpath:sales-app.properties -file:${catalina.home}/conf/app-core/local.app.properties - Здесь префикс classpath: означает, что данный файл нужно искать в Java classpath, префикс file: − в файловой системе. Возможно использование системных свойств Java, в данном случае это catalina.home − путь к корню Tomcat. - Порядок перечисления файлов важен, так как значения, указанные в каждом последующем файле заменяют значения одноименных свойств, заданные в предыдущих файлах. Этим достигается переопределение свойств платформы в конкретном приложении. - Последний файл в приведенном наборе − local.app.properties. Он может использоваться для переопределения свойств приложения при развертывании. Если этого файла нет, он игнорируется. Если же во время инсталляции системы требуется переопределение некоторых параметров (как правило различных URL), достаточно создать этот файл и поместить в него переопределяемые свойства. При последующих обновлениях системы такой файл с локальными настройками легко сохранить. - Аналогом local.app.properties для Desktop Client служат аргументы командной строки запуска JVM. Загрузчик свойств данного блока воспринимает все аргументы, содержащие знак "=", как пары ключ-значение, и заменяет ими соответствующие свойства приложения, заданные в файлах app.properties. - - Правила задания информации в файлах *.properties: - - - Кодировка файла - UTF-8 - - - Ключ может состоять из латинских букв, цифр, точек и знаков подчеркивания - - - Значение пишется после знака равно (=) - - - Значение не нужно брать в кавычки " или ' - - - Файловые пути записываются либо в UNIX виде (/opt/haulmont/), либо в Windows виде (c:\\haulmont\\) - - - Возможно использование кодов \n \t \r. Символ \ является зарезервированным, для вставки в значение экранируется сам собой (\\). -Подробнее см.: http://docs.oracle.com/javase/tutorial/java/data/characters.html - - - Для ввода значения в нескольких строках файла используйте символ \ в конце строки, для того чтобы данное значение продолжалось на следующей строке. - - - -
-
- Хранение свойств в базе данных - Параметры времени выполнения хранятся в таблице SYS_CONFIG базы данных. - Такие свойства имеют следующие особенности: - - так как значение свойства хранится в базе данных, оно задается в одном месте, независимо от того, в каких блоках приложения оно используется - - - значение может быть изменено и сохранено во время работы приложения, как через конфигурационный интерфейс, содержащий это свойство, так и через ConfigStorageMBean. - - - Хранящиеся в БД свойства кэшируются на уровне Middleware. Очистить кэш можно с помощью JMX-интерфейсов - ConfigStorageMBean - методом clearCache() или - CachingFacadeMBean - методом clearConfigStorageCache(). - Следует иметь в виду, что на клиентском уровне чтение свойства, хранящегося в БД, приводит к запросу к Middleware, что менее эффективно, чем чтение локального свойства из app.properties. Для уменьшения количества таких запросов клиент кэширует все свойства, хранящиеся в БД, на время жизни экземпляра реализации конфигурационного интерфейса. Поэтому если например в некотором экране UI необходимо несколько раз обратиться к свойствам одного конфигурационного интерфейса, лучше получить ссылку на него при инициализации экрана, и сохранить в поле для последующих обращений к одному и тому же экземпляру. -
-
- Конфигурационные интерфейсы - Данный механизм позволяет работать со свойствами приложения через методы Java-интерфейсов, что дает следующие преимущества: - - типизированность - прикладной код работает с нужными типами (String, Boolean, Integer и пр.), а не только со строками - - - в прикладном коде вместо строковых идентификаторов свойств используются методы интерфейсов, имена которых подсказываются средой разработки - - - Пример получения значения таймаута транзакции в блоке Middleware:@Inject -protected Configuration configuration; -... -ServerConfig serverConfig = configuration.getConfig(ServerConfig.class); -int timeout = serverConfig.getDefaultQueryTimeoutSec(); - Или при невозможности инжекции Configuration:int timeout = AppBeans.get(Configuration.class) - .getConfig(ServerConfig.class) - .getDefaultQueryTimeoutSec(); -
- Использование - Для создания конфигурационного интерфейса необходимо: - - - Создать интерфейс, унаследованный от com.haulmont.cuba.core.config.Config (не путать с классом сущности com.haulmont.cuba.core.entity.Config) - - - Добавить интерфейсу аннотацию @Source для указания источника (способа хранения) параметров: - - SourceType.SYSTEM - значение свойства будет взято из системных свойств данной JVM, т.е. методом System.getProperty() - - - SourceType.APP - значение свойства будет взято из файлов app.properties - - - SourceType.DATABASE - значение свойства будет взято из таблицы SYS_CONFIG - - - - - Создать методы доступа к свойству (getter / settter). Если значение свойства не предполагается изменять во время выполнения, метод доступа на запись не нужен. Возможный тип свойства рассмотрен ниже. - - - Добавить методу доступа на чтение аннотацию @Property, определяющую имя свойства. - - - Опционально аннотацию @Source можно задать для отдельного свойства в интерфейсе, если его источник отличается от заданного для всего интерфейса. - - - Например:@Source(type = SourceType.DATABASE) -public interface SalesConfig extends Config { - - @Property("sales.companyName") - String getCompanyName(); -} - Создавать класс реализации конфигурационного интерфейса не нужно - при получении ссылки на интерфейс через Configuration будет автоматически создан необходимый прокси-объект. -
-
- Типы свойств - Без дополнительных усилий поддерживаются следующие типы свойств: - - String - - - простые типы либо их объектные обертки (boolean, Boolean, int, Integer, etc.) - - - классы персистентных сущностей. При обращении к свойству типа сущности происходит загрузка из БД экземпляра, заданного значением свойства. - - - Для поддержки произвольного типа необходимо реализовать классы TypeStringify и TypeFactory для преобразования значения в строку и из нее, и указать эти классы для свойства с помощью аннотаций @Stringify и @Factory. - Рассмотрим этот процесс на примере типа UUID. - - - Создаем класс com.haulmont.cuba.core.config.type.UuidTypeFactory унаследованный от com.haulmont.cuba.core.config.type.TypeFactory и реализуем в нем метод:public Object build(String string) { - if (string == null) - return null; - return UUID.fromString(string); -} - - - TypeStringify создавать не нужно, т.к. по умолчанию будет использован метод toString() − в данном случае он нам подходит. - - - Аннотируем свойство в конфигурационном интерфейсе:@Factory(factory = UuidTypeFactory.class) -UUID getUuidProp(); -void setUuidProp(UUID value); - - -
-
- Значения по умолчанию - Для свойств конфигурационных интерфейсов могут быть заданы значения по умолчанию. Эти значения будут возвращаться, если данный параметр не задан в месте хранения - в БД или в app.properties. - Значение по умолчанию может быть задано в виде строки с помощью аннотации @Default, либо в виде конкретного типа с помощью других аннотаций пакета com.haulmont.cuba.core.config.defaults:@Property("cuba.email.adminAddress") -@Default("address@company.com") -String getAdminAddress(); - -@Property("cuba.email.delayCallCount") -@Default("2") -int getDelayCallCount(); - -@Property("cuba.email.defaultSendingAttemptsCount") -@DefaultInt(10) -int getDefaultSendingAttemptsCount(); - Для сущностей значение по умолчанию задается строкой вида {entity_name}-{id}-{optional_view_name}, например:@Default("sec$User-98e5e66c-3ac9-11e2-94c1-3860770d7eaf-browse") -User getAdminUser(); - -@Default("sec$Role-a294aef0-3ac9-11e2-9433-3860770d7eaf") -Role getAdminRole(); -
-
-
-
- Локализация сообщений - Приложение на основе платформы CUBA поддерживает локализацию сообщений, то есть вывод всех элементов пользовательского интерфейса на языке, выбранном пользователем. - Возможности выбора языка определяются комбинацией свойств приложения cuba.localeSelectVisible и cuba.availableLocales. - Для того, чтобы некоторое сообщение могло быть локализовано, т.е. представлено пользователю на нужном языке, его необходимо поместить в так называемый пакет сообщений. -
- Пакеты сообщений - Пакет сообщений представляет собой набор файлов свойств с именами вида messages{_XX}.properties, расположенных в одном Java-пакете. Суффикс XX определяет язык, для которого в данном файле содержатся сообщения, и соответствует коду языка в Locale.getLanguage(). Один из файлов пакета может быть без суффикса языка - это файл по умолчанию. Именем пакета сообщений считается имя Java-пакета, в котором расположены файлы пакета. - Рассмотрим пример:/com/abc/sales/gui/customer/messages.properties -/com/abc/sales/gui/customer/messages_fr.properties -/com/abc/sales/gui/customer/messages_ru.properties - Данный пакет состоит из 3-х файлов - один для русского языка, один для французского, и один по умолчанию. Имя пакета - com.abc.sales.gui.customer - Файлы сообщений содержат пары ключ-значение, где ключ - это идентификатор сообщения, на который ссылается код приложения, а значение - само сообщение на языке данного файла. Правила задания пар аналогичны правилам файлов свойствjava.util.Properties, со следующими особенностями: - - Кодировка файла - обязательно UTF-8 - - - Поддерживается включение других пакетов сообщений с помощью ключа @include, в том числе нескольких сразу - перечислением через запятую. При этом если некоторый ключ сообщения встречается и во включаемом пакете, и в текущем, будет использовано сообщение из текущего. Пример включения пакетов:@include=com.haulmont.cuba.web, com.abc.sales.web - -someMessage=Some Message -... - - - Получение сообщений из пакетов производится с помощью методов интерфейса - Messages - по следующим правилам: - - Сначала производится поиск в конфигурационном каталоге приложения - - Ищется файл messages_XX.properties в каталоге, задаваемом именем пакета сообщений, где XX - код требуемого языка - - - Если такого файла нет, в этом же каталоге ищется файл по умолчанию messages.properties - - - Если найден или файл нужного языка, или файл по умолчанию, он загружается вместе со всеми @include, и в нем ищется ключ сообщения - - - Если файл не найден, либо нужный ключ в нем отсутствует, производится смена каталога на родительский, и процедура поиска повторяется. И так до достижения корня конфигурационного каталога. - - - - - Если в конфигурационном каталоге сообщение не найдено, производится поиск в classpath по такому-же алгоритму. - - - На клиентском уровне, если сообщение не найдено на предыдущих шагах, отправляется запрос на Middleware, и сообщение ищется там аналогичным способом. - - - Если сообщение найдено, оно кэшируется и возвращается. Если не найдено - кэшируется факт отсутствия сообщения и возвращается ключ, который был передан для поиска. Таким образом, сложная процедура поиска выполняется только один раз, в дальнейшем результат загружается из локального для блока приложения кэша. - - - - Рекомендуется организовывать пакеты сообщений следующим образом: - - Если приложение не предполагает интернационализации, то можно не использовать пакеты и включать строки сообщений прямо в код приложения, либо пользоваться файлами по умолчанию messages.properties для отделения ресурсов от кода. - - - Если приложение интернациональное, логично файлы по умолчанию использовать для языка основной аудитории приложения, либо для английского языка. Именно сообщения из файлов по умолчанию будут показаны пользователю, если сообщений для нужного языка не найдено. - - - -
-
- Главный пакет сообщений - Каждый стандартный блок приложения определяет для себя один главный пакет сообщений. Для блоков клиентского уровня этот пакет содержит названия пунктов главного меню и общих элементов UI (например названия кнопок OK и Cancel). Для всех блоков приложения, включая Midleware, главный пакет определяет форматы преобразований - Datatype - . - Для указания главного пакета соощений используется свойство приложения - cuba.mainMessagePack - . Значением свойства может быть либо один пакет, либо список пакетов, разделенный пробелами. Например:cuba.mainMessagePack=com.haulmont.cuba.web com.abc.sales.web - В данном случае сообщения, заданные во втором пакете списка будут перекрывать сообщений из первого пакета. Таким образом в проекте приложения можно переопределять сообщения, заданные в пакетах базовых проектов. -
-
- Локализация названий сущностей и атрибутов - Для отображения в UI локализованных названий сущностей и их атрибутов необходимо создать специальные пакеты сообщений в тех же Java-пакетах, что и сами сущности. Формат файлов сообщений должен быть следующим: - - Ключ названия сущности - простое имя класса (без пакета) - - - Ключ названия атрибута - простое имя класса, затем через точку имя атрибута - - - Пример русской локализации сущнсти com.abc.sales.entity.Customer - файл /com/abc/sales/entity/messages_ru.properties:Customer=Покупатель -Customer.name=Имя -Customer.email=Email - -Order=Заказ -Order.customer=Покупатель -Order.date=Дата -Order.amount=Сумма - Такие пакеты сообщений как правило используются неявно для разработчика, например визуальными компонентами - Table - и - FieldGroup - . Кроме того, названия сущностей и атрибутов могут быть также получены следующими методами: - - программно - методами - MessageTools - getEntityCaption(), getPropertyCaption() - - - в XML дескрипторе экрана - указанием ссылки на сообщение по правилам MessageTools.loadString(): msg://{entity_package}/{key}, например caption="msg://com.abc.sales.entity/Customer.name" - - -
-
- Локализация enum - Для локализации названий и значений перечислений необходимо в пакет сообщений, находящийся в Java-пакете класса перечисления добавить сообщения со следующими ключами: - - Ключ названия перечисления - простое имя класса (без пакета) - - - Ключ значения - простое имя класса, затем через точку имя значения - - - Например, для перечисления package com.abc.sales; - -public enum CustomerGrade { PREMIUM, HIGH, STANDARD } - файл русской локализации /com/abc/sales/messages_ru.properties должен содержать строки:CustomerGrade=Уровень покупателя -CustomerGrade.PREMIUM=Премиум -CustomerGrade.HIGH=Высокий -CustomerGrade.STANDARD=Стандартный - Локализованные значения перечислений автоматически используются различными визуальными компонентами, например - LookupField - . Для программного получения локализованного значения перечисления можно использовать метод getMessage() интерфейса - Messages - , просто передавая в него экземпляр enum. -
-
-
- Аутентификация пользователей - В данном разделе рассмотрены некоторые аспекты управления доступом с точки зрения разработчика приложения. Для получения полной информации о возможностях и настройке доступа пользователей к данным обратитесь к руководству Платформа CUBA. Подсистема безопасности. -
- UserSession - Основной элемент подсистемы контроля доступа в CUBA-приложении - пользовательская сессия. Это объект класса UserSession, который ассоциирован с аутентифицированным в данный момент в системе пользователем, и содержит информацию о правах доступа пользователя к данным. Объект текущей сессии может быть получен в любом блоке приложения через интерфейс инфраструктуры - UserSessionSource - . - Пользовательская сессия создается на Middleware при выполнении метода LoginService.login() после аутентификации пользователя по переданному имени и паролю. Объект UserSession затем кэшируется в данном блоке Middleware, и возвращается на клиентский уровень. Пр работе в кластере объект сессии реплицируется на соседние узлы кластера Middleware. Клиентский блок, получив объект сессии, также сохраняет его у себя, так или иначе ассоциируя с активным пользователем (например в HTTP сессии). Далее все вызовы Middleware для данного пользователя сопровождаются передачей идентификатора сессии (типа UUID), причем прикладному коду не нужно об этом заботиться - идентификатор сессии передается автоматически, независимо от сигнатуры вызываемых методов среднего слоя. Обработка вызовов клиентов на Middleware начинается с извлечения из кэша сессии по полученному идентификатору и установки ее в потоке выполнения. Объект сессии удаляется из кэша при вызове метода LoginService.logout(), либо при истечения времени бездействия, определяемого свойством приложения - cuba.userSessionExpirationTimeoutSec - . - Таким образом, идентификатор сессии, создаваемой при входе пользователя в систему, служит для аутентификации пользователя при каждом вызове среднего слоя. - Объект UserSession содержит также методы для авторизации текущего пользователя, т.е. проверки его прав на объекты системы: isScreenPermitted(), isEntityOpPermitted(), isEntityAttrPermitted(), isSpecificPermitted(). -
-
- Вход в систему - Стандартный вариант входа пользователя: - - пользователь вводит свой логин и пароль - - - клиентский блок приложения хэширует пароль, вызывая метод getPlainHash() бина PasswordEncryption и вызывает на Middleware метод LoginService.login(), передавая ему логин пользователя и хэш пароля - - - LoginService делегирует выполнение бину LoginWorker, который загружает объект User по полученному логину, хэширует полученный хэш пароля повторно, используя в качестве соли идентификатор пользователя, и сравнивает полученный хэш с сохраненным в БД хэшем пароля. В случае несовпадения выбрасывается исключение LoginException. - - - После успешной аутентификации в созданный экземпляр - UserSession - загружаются все параметры доступа данного пользователя: список ролей, права, ограничения и атрибуты сессии. - - - Алгоритм хэширования паролей реализуется бином типа EncryptionModule и задается в свойстве приложения - cuba.passwordEncryptionModule - . По умолчанию - SHA-1. - Возможен вариант, когда пароль пользователя (точнее, хэш пароля) не хранится в базе данных, а проверяется внешними средствами, например путем интеграции с ActiveDirectory. В этом случае фактически аутентификацию выполняет клиентский блок, а Middleware "доверяет" клиенту, создавая сессию по одному только логину пользователя без пароля методом LoginService.loginTrusted(). Метод loginTrusted() требует выполнения следующих условия: - - клиентский блок должен передать так называемый доверенный пароль, задаваемый на Middleware и на клиентском блоке свойством приложения - cuba.trustedClientPassword - - - - IP адрес клиентского блока должен соответствовать маске, задаваемой свойством приложения - cuba.trustedClientPermittedIpMask - - - - Вход в систему требуется также для автоматических процессов, запускаемых по расписанию, а также при подключении к бинам Middleware через JMX-интерфейс. Строго говоря, такие действия считаются административными и не требуют аутентификации до тех пор, пока не выполняется каких-либо изменений сущностей в базе данных. При записи сущностей в БД требуется проставить логин пользователя, который выполнил изменения, поэтому для работы таких процессов должен быть указан пользователь, от лица которого выполняются изменения. - Дополнительным плюсом входа в систему для автоматического процесса и для JMX-вызова является то, что вывод в журнал сообщений от логгеров сопровождается указанием логина текущего пользователя, если пользовательская сессия установлена в потоке выполнения. Это упрощает поиск сообщений от конкретного процесса при разборе журнала. - Вход в систему для процессов внутри Middleware выполняется вызовом LoginWorker.loginSystem() с передачей логина пользователя (без пароля), от имени которого будет работать данный процесс. В результате создается объект - UserSession - , который будет закэширован в данном блоке Middleware и не будет реплицироваться в кластере. - Стандартная реализация входа / выхода для компонентов Middleware заключена в специальном базовом классе - ManagementBean - , поэтому свои JMX-бины рекомендуется наследовать от него. -
-
- SecurityContext - Экземпляр класса SecurityContext хранит информацию о пользовательской сессии для текущего потока выполнения. Он создается и передается в метод AppContext.setSecurityContext() в следующие моменты: - - для блоков Web Client и Web Portal - в начале обработки каждого HTTP-запроса от пользовательского браузера - - - для блока Middleware - в начале обработки каждого запроса от клиентского уровня - - - для блока Desktop Client - один раз после входа пользователя, так как десктопное приложение является однопользовательским - - - По окончании выполнения запроса в первых двух случаях SecurityContext удаляется из потока выполнения. - При создании прикладным кодом нового потока выполнения в него необходимо передать текущий экземпляр SecurityContext, например:final SecurityContext securityContext = AppContext.getSecurityContext(); -executor.submit(new Runnable() { - public void run() { - AppContext.setSecurityContext(securityContext); - // business logic here - } -}); -
-
-
-
- Компоненты среднего слоя - На следующем рисунке приведены основные компоненты среднего слоя CUBA-приложения. -
- Компоненты среднего слоя - - - - - -
- Services – управляемые контейнером компоненты, формирующие границу приложения и предоставляющие интерфейс клиентскому уровню приложения. Сервисы могут содержать бизнес-логику сами, либо делегировать выполнение Managed Beans. - Managed Beans – управляемые контейнером компоненты, содержащие бизнес-логику приложения. Вызываются сервисами, другими бинами или через опциональный JMX интерфейс. - - Persistence - − инфраструктурный интерфейс для доступа к функциональности хранения данных: управлению транзакциями и ORM. -
- Сервисы - Сервисы образуют слой компонентов, определяющий множество операций Middleware, доступных клиентскому уровню приложения. Внутри сервисов инкапсулируется бизнес-логика и управление транзакциями. - Основные задачи сервисов: - - - Предоставляют удаленный (remote) интерфейс для вызова с клиентского уровня - - - Проверяют наличие активной пользовательской сессии, соответствующей идентификатору сессии, переданному с клиента - - - Записывают в журнал необработанные исключения среднего слоя - - - Кроме того, именно в слое сервисов рекомендуется выполнять авторизацию текущего пользователя, т.е. проверять его права на ту или иную функциональность. - Общие для всех сервисов задачи решаются следующим образом: - - Проверка наличия пользовательской сессии и логгирование исключений производится классом-интерцептором ServiceInterceptor, который перехватывает выполнение каждого метода сервиса с помощью Spring AOP - - - Удаленный интерфейс для доступа к сервису через Spring HTTP Invoker создается бином RemoteServicesBeanCreator, который конфигурируется в файле - remoting-spring.xml - модуля core. - - -
- Диаграмма классов сервиса - - - - - -
-
- Создание сервиса - Имена интерфейсов сервисов должны заканчиваться на Service, имена классов реализации на ServiceBean. - При создании сервиса необходимо выполнить следующее: - - - Создать интерфейс в модуле global (т.к. интерфейс сервиса должен быть доступен на всех уровнях) и задать в нем имя сервиса. Имя рекомендуется задавать в формате {имя_проекта}_{имя_интерфейса}. Например: - package com.sample.sales.core; - -import com.sample.sales.entity.Order; - -public interface OrderService { - String NAME = "sales_OrderService"; - - void calculateTotals(Order order); -} - - - Создать класс сервиса в модуле core и добавить ему аннотацию @org.springframework.stereotype.Service с именем, заданным в интерфейсе - package com.sample.sales.core; - -import com.sample.sales.entity.Order; -import org.springframework.stereotype.Service; - -@Service(OrderService.NAME) -public class OrderServiceBean implements OrderService { - @Override - public void calculateTotals(Order order) { - } -} - Класс сервиса, как и класс любого другого управляемого бина, должен находиться внутри дерева пакетов с корнем, заданным в элементе context:component-scan файла - spring.xml - . В нашем случае файл sales-spring.xml содержит элемент:<context:component-scan base-package="com.sample.sales"/>что означает, что поиск аннотированных бинов для данного блока приложения будет происходить начиная с пакета com.sample.sales. - - - - Сервисы предназначены только для вызова "снаружи" Middleware. Не рекомендуется вызывать методы сервисов из других компонентов среднего слоя. При обнаружении факта вызова сервиса из другого сервиса в журнал выводится сообщение об ошибке. - Если некоторую бизнес-логику требуется вызывать из разных сервисов либо других компонентов Middleware, ее необходимо выделить и инкапсулировать внутри соответствующего Managed Bean. - -
-
- Использование сервиса - Для того чтобы вызывать сервис, в клиентском блоке приложения для него должен быть создан соответствующий прокси-объект. Делается это путем объявления имени и интерфейса сервиса в параметрах фабрики прокси-объектов. Для блока Web Client это бин класса WebRemoteProxyBeanCreator, для Web Portal - PortalRemoteProxyBeanCreator , для Desktop Client - RemoteProxyBeanCreator . - Фабрика прокси-объектов конфигурируется в файле - spring.xml - ссответствующего клиентского блока. - Например, чтобы в приложении sales вызвать с веб-клиента сервис sales_OrderService, необходимо добавить в файл sales-web-spring.xml модуля web следующее: - <bean id="sales_proxyCreator" class="com.haulmont.cuba.web.sys.remoting.WebRemoteProxyBeanCreator"> - <property name="clusterInvocationSupport" ref="cuba_clusterInvocationSupport"/> - <property name="remoteServices"> - <map> - <entry key="sales_OrderService" value="com.sample.sales.core.OrderService"/> - </map> - </property> -</bean> - Все импортируемые сервисы объявляются в одном свойстве remoteServices в элементах map/entry. - С точки зрения прикладного кода прокси-объект сервиса на клиентском уровне является обычным бином Spring и может быть получен либо инжекцией, либо с помощью класса AppBeans, например:@Inject -protected OrderService orderService; -... -orderService.calculateTotals(order); -
-
-
- Управляемые бины - Управляемые бины (Managed Beans) − это программные компоненты, предназначенные для реализации бизнес-логики приложения. Термин "управляемые" в данном случае означает, что созданием экземпляров и установкой связей между такими компонентами управляет контейнер, который является основной частью фреймворка Sping. - - Managed Bean представляет собой singleton, то есть в некотором блоке приложения существует только один экземпляр данного класса. Поэтому, если бин содержит изменяемые данные в полях (другими словами, имеет состояние), то обращение к таким данным необходимо синхронизировать. - -
- Создание бина - Для создания управляемого бина достаточно добавить классу Java аннотацию @javax.annotation.ManagedBean. Например:package com.sample.sales.core; - -import com.sample.sales.entity.Order; -import javax.annotation.ManagedBean; - -@ManagedBean(OrderWorker.NAME) -public class OrderWorker { - public static final String NAME = "sales_OrderWorker"; - - public void calculateTotals(Order order) { - } -} - Рекомендуется присваивать бину уникальное имя вида {имя_проекта}_{имя_класса}, и определять его в константе NAME. - Класс управляемого бина должен находиться внутри дерева пакетов с корнем, заданным в элементе context:component-scan файла - spring.xml - . В нашем случае файл sales-spring.xml содержит элемент:<context:component-scan base-package="com.sample.sales"/>что означает, что поиск аннотированных бинов для данного блока приложения будет происходить начиная с пакета com.sample.sales. - Если нужно обеспечить возможность подмены реализации, рекомендуется выделять бизнес-интерфейс бина, например следующим образом: - package com.sample.sales.core; - -import com.sample.sales.entity.Order; - -public interface OrderWorker { - String NAME = "sales_OrderWorker"; - - void calculateTotals(Order order); -} - package com.sample.sales.core; - -import com.sample.sales.entity.Order; -import javax.annotation.ManagedBean; - -@ManagedBean(OrderWorker.NAME) -public class OrderWorkerBean implements OrderWorker { - @Override - public void calculateTotals(Order order) { - } -} - Управляемые бины можно создавать на любом уровне, так как контейнер Spring Framework используется во всех стандартные блоках приложения. -
-
- Использование бина - Ссылку на бин можно получить с помощью инжекции или класса AppBeans. В качестве примера использования бина рассмотрим реализацию сервиса OrderService, делегирующего выполнение бину OrderWorker:package com.sample.sales.core; - -import com.haulmont.cuba.core.Persistence; -import com.sample.sales.entity.Order; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import javax.inject.Inject; - -@Service(OrderService.NAME) -public class OrderServiceBean implements OrderService { - - @Inject - protected Persistence persistence; - - @Inject - protected OrderWorker orderWorker; - - @Transactional - @Override - public BigDecimal calculateTotals(Order order) { - Order entity = persistence.getEntityManager().merge(order); - orderWorker.calculateTotals(entity); - } -} - В данном примере сервис стартует транзакцию, вносит полученный с клиентского уровня экземпляр сущности в персистентный контекст, и передает управление бину OrderWorker, который и содержит основную бизнес-логику. -
-
-
- JMX-бины - Иногда требуется предоставить администратору системы возможность просматривать и изменять состояние некоторого управляемого бина во время выполнения. В этом случае рекомендуется создать JMX-бин - программный компонент, имеющий JMX-интерфейс. Такой бин как правило делегирует вызовы управляемому бину, содержащему кэш, конфигурационные данные или статистику, к которым нужно обеспечить доступ через JMX. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/JMXBeans.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Как видно из диаграммы, JMX-бин состоит из интерфейса и класса реализации. Класс должен представлять собой <link linkend="managed_beans">управляемый бин</link>, то есть иметь аннотацию <code>@ManagedBean</code> и уникальное имя. Интерфейс JMX-бина специальным образом регистрируется в <link linkend="spring.xml">spring.xml</link> для создания в текущей JVM собственно JMX-интерфейса.</para> - <para>Вызовы всех методов интерфейса JMX-бина перехватываются с помощью <application>Spring AOP</application> классом−<glossterm linkend="interceptor">интерцептором</glossterm> <methodname>MBeanInterceptor</methodname>, который обеспечивает установку правильного <code>ClassLoader</code> в контексте потока выполнения, и журналирование необработанных исключений.</para> - <warning> - <para>Интерфейс JMX-бина обязательно должен иметь имя вида <literal>{имя_класса}MBean</literal>.</para> - </warning> - <section> - <title>Создание JMX-бина - Рассмотрим процесс создания JMX-бина на примере. - - - Интерфейс JMX-бина:package com.sample.sales.core; - -import org.springframework.jmx.export.annotation.*; - -@ManagedResource(description = "Performs operations on Orders") -public interface OrdersMBean { - - @ManagedOperation(description = "Recalculates an order amount") - @ManagedOperationParameters({@ManagedOperationParameter(name = "orderId", description = "")}) - String calculateTotals(String orderId); -} - Интерфейс и его методы могут содержать аннотации для задания описания JMX-бина и его операций. Это описание будет отображаться во всех инструментах, работающих с данным JMX-интерфейсом, тем самым помогая администратору системы. - Так как инструменты JMX поддерживают ограниченный набор типов данных, параметры и результат метода желательно задавать типа String, и при необходимости выполнять конвертацию внутри метода. - - - Класс JMX-бина:package com.sample.sales.core; - -import com.haulmont.cuba.core.*; -import com.haulmont.cuba.core.app.*; -import com.sample.sales.entity.Order; -import org.apache.commons.lang.exception.ExceptionUtils; -import javax.annotation.ManagedBean; -import javax.inject.Inject; -import java.util.UUID; - -@ManagedBean("sales_OrdersMBean") -public class Orders implements OrdersMBean { - - @Inject - protected OrderWorker orderWorker; - - @Inject - protected Persistence persistence; - - @Authenticated - @Override - public String calculateTotals(final String orderId) { - try { - persistence.createTransaction().execute(new Transaction.Runnable() { - @Override - public void run(EntityManager em) { - Order entity = em.find(Order.class, UUID.fromString(orderId)); - orderWorker.calculateTotals(entity); - } - }); - return "Done"; - } catch (Throwable e) { - return ExceptionUtils.getStackTrace(e); - } - } -} - Аннотация @ManagedBean определяет, что данный класс является управляемым бином с именем sales_OrdersMBean. Имя указано напрямую в аннотации, а не в константе, так как доступ к JMX-бину из кода Java не требуется. - Рассмотрим реализацию метода calculateTotals(). - - Метод имеет аннотацию @Authenticated, т.е. при входе в метод и при отсутствии в потоке выполнения пользовательской сессии выполняется системная аутентификация. - - - Тело метода обернуто в блок try/catch, так что метод в случае успешного выполнения возвращает строку "Done", а в случае ошибки - stacktrace исключения в виде строки. - Следует иметь в виду, что в данном случае все исключения обрабатываются, а значит, не попадают в MBeanInterceptor и не выводятся в журнал автоматически. Поэтому при необходимости логгировать исключения здесь нужно добавить вызов логгера в секции catch. - - - Логика метода заключается в том, что он стартует транзакцию, загружает экземпляр сущности Order по идентификатору, и передает управление бину OrderWorker для обработки. - - - - - Регистрация JMX-бина в sales-spring.xml: - <bean id="sales_MBeanExporter" lazy-init="false" - class="com.haulmont.cuba.jmxcontrol.export.MBeanExporter"> - <property name="beans"> - <map> - <entry key="${cuba.webContextName}.sales:type=Orders" - value-ref="sales_OrdersMBean"/> - </map> - </property> -</bean> - - - Все JMX-бины проекта объявляются в одном экземпляре MBeanExporter в элементах map/entry свойства beans. Ключом элемента здесь является JMX ObjectName, значением - имя бина, заданное -в аннотации @ManagedBean. ObjectName начинается с имени веб-приложения, так как в одном экземпляре Tomcat (т.е. в одной JVM) может быть развернуто несколько веб-приложений, экспортирующих одинаковые JMX-интерфейсы. -
-
- JMX-бины платформы - В данном разделе описаны некоторые имеющиеся в платформе JMX-бины. -
- CachingFacadeMBean - CachingFacadeMBean предоставляет методы очистки различных кэшей в блоках Middleware и Web Client. - JMX ObjectName: app-core.cuba:type=CachingFacade и app.cuba:type=CachingFacade -
-
- ConfigStorageMBean - ConfigStorageMBean позволяет просматривать и задавать значения свойствам приложения в блоках Middleware, Web Client и Web Portal. - Следует иметь в виду, что измененные значения для свойств, хранящихся в файлах, не сохраняются, и действуют только до рестарта данного блока. - JMX ObjectName: app-core.cuba:type=ConfigStorage, app.cuba:type=ConfigStorage, app-portal.cuba:type=ConfigStorage -
-
- PersistenceManagerMBean - PersistenceManagerMBean предоставляет следующие возможности: - - управление механизмом статистики сущностей - - - отображение новых скриптов обновления БД методом findUpdateDatabaseScripts() и запуск обновления методом updateDatabase() - - - запуск произвольных JPQL запросов в контексте Middleware методами jpqlLoadList(), jpqlExecuteUpdate() - - - JMX ObjectName: app-core.cuba:type=PersistenceManager -
-
- ScriptingManagerMBean - ScriptingManagerMBean является JMX-фасадом для интерфейса инфраструктуры - Scripting - . - JMX ObjectName: app-core.cuba:type=ScriptingManager - JMX атрибуты: - - RootPath - абсолютный путь к конфигурационному каталогу блока приложения, в котором запущен данный бин - - - JMX операции: - - runGroovyScript() - выполнить скрипт Groovy в контексте Middleware и вернуть результат. Для отображения в JMX-интерфейсе результат должен быть типа String. В остальном аналогичен методу Scripting.runGroovyScript(). - - -
-
-
-
- Системная аутентификация - При выполнении пользовательских запросов программному коду Middleware через интерфейс - UserSessionSource - всегда доступна информация о текущем пользователе. Это возможно потому, что при получении запроса с клиентского уровня в потоке выполнения автоматически устанавливается соответствующий объект - SecurityContext - . - Однако существуют ситуации, когда текущий поток выполнения не связан ни с каким пользователем системы: например, при вызове метода бина из планировщика, либо через JMX-интерфейс. Если при этом бин выполняет изменение сущностей в базе данных, то ему потребуется информация о том, кто выполняет изменения, то есть аутентификация. - Такого рода аутентификация называется системной, так как не требует участия пользователя - средний слой приложения просто создает (или использует имеющуюся) пользовательскую сессию, и устанавливает в потоке выполнения соответствующий объект SecurityContext. - Обеспечить системную аутентификацию некоторого участка кода можно следующими способами: - - явно используя бин com.haulmont.cuba.security.app.Authentication, например:@Inject -protected Authentication authentication; -... -authentication.begin(); -try { - // authenticated code -} finally { - authentication.end(); -} - - - добавив методу бина аннотацию @Authenticated, например:@Authenticated -public String foo(String value) { - // authenticated code -} - - - Во втором случае также используется бин Authentication, но неявно, через интерцептор AuthenticationInterceptor, который перехватывает вызовы всех методов бинов с аннотацией @Authenticated. - В приведенных примерах пользовательская сессия будет создаваться от лица пользователя, логин которого указан в свойстве приложения - cuba.jmxUserLogin - . Если требуется аутентификация от имени другого пользователя, нужно воспользоваться первым вариантом и передать в метод begin() логин нужного пользователя. - - Если в момент выполнения Authentication.begin() в текущем потоке выполнения присутствует активная пользовательская сессия, то она не заменяется - соответственно, код, требующий аутентификации, будет выполняться с имеющейся сессией, и последующий метод end() не будет очищать поток. - Следует иметь в виду, что вызов метода JMX-бина из встроенной в Web Client консоли JMX является обычной обработкой пользовательского запроса. Это означает, что метод JMX-бина будет выполнен от имени текущего зарегистрированного в системе пользователя, независимо от наличия системной аутентификации. - -
-
- Слой ORM -
- Общие сведения - Object-Relational Mapping - объектно-реляционное отображение - технология связывания таблиц реляционной базы данных с объектами языка программирования. - - - Преимущества использования ORM: - - - - Позволяет работать с данными реляционной СУБД, манипулируя объектами Java - - - Упрощает программирование, избавляя от рутины написания тривиальных SQL-запросов - - - Упрощает программирование, позволяя извлекать и сохранять целые графы объектов одной командой - - - Обеспечивает легкое портирование приложения на различные СУБД - - - Использует лаконичный язык запросов JPQL - - - Оптимизирует количество выполняемых SQL-запросов на команды insert и update - - - - - - Недостатки: - - - - Требует понимания особенностей работы с ORM - - - Не позволяет напрямую оптимизировать SQL или использовать особенности применяемой СУБД - - - - - - В платформе CUBA используется реализация ORM по стандарту Java Persistence API на основе фреймворка Apache OpenJPA. -
-
- EntityManager - EntityManager - основной интерфейс ORM, служит для управления персистентными сущностями. - - Ссылку на EntityManager можно получить через интерфейс Persistence, вызовом метода getEntityManager(). -Полученный экземпляр EntityManager привязан к текущей транзакции, то есть все вызовы getEntityManager() в рамках одной транзакции возвращают один и тот же экземпляр EntityManager. После завершения транзакции обращения к данному экземпляру невозможны. - - Экземпляр EntityManager содержит в себе "персистентный контекст" – набор экземпляров сущностей, загруженных из БД или только что созданных. Персистентный контекст является своего рода кэшем данных в рамках транзакции. -EntityManager автоматически сбрасывает в БД все изменения, сделанные в его персистентном контексте, в момент коммита транзакции, либо при явном вызове метода flush(). - Интерфейс EntityManager, используемый в CUBA-приложениях, в основном повторяет стандартный javax.persistence.EntityManager. Рассмотрим специфичные методы: - - setView() - устанавливает представление, с которым будет производиться последующая загрузка сущностей методом find() либо JPQL запросами. В результате энергично загружены будут все не-lazy атрибуты представления. - Если в данный метод передать null, либо не вызывать его вообще, загрузка будет производиться в соответствие с правилами аннотаций сущностей. - - - addView() - аналогичен методу setView(), но в случае наличия уже установленного в EntityManager представления, не заменяет его, а добавляет атрибуты переданного представления. - - - fetch() - обеспечивает для экземпляра сущности загрузку всех атрибутов указанного представления, включая lazy атрибуты. Экземпляр сущности должен быть в Managed состоянии. - Данный метод рекомендуется вызывать перед коммитом транзакции, если представление содержит lazy атрибуты, а экземпляр сущности нужно отправить на клиентский уровень. В этом случае только после вызова fetch() можно быть уверенным, что все нужные клиентсткому коду атрибуты действительно загружены. - - - isSoftDeletion() - проверяет, находится ли данный EntityManager в режиме мягкого удаления. - - - setSoftDeletion() - устанавливает режим мягкого удаления для данного экземпляра EntityManager. - - - getConnection() - возвращает java.sql.Connection, через который выполняет запросы данный экземпляр EntityManager, и, соответственно, текущая транзакция. Закрывать такое соединение не нужно, оно будет закрыто при завершении транзакции. - - - getDelegate() - возвращает javax.persistence.EntityManager, предоставляемый реализацией ORM. - - -
-
- Состояния сущности - - - New - - Только что созданный в памяти экземпляр, например: Car car = new Car() - Новый экземпляр может быть передан в EntityManager.persist() для сохранения в БД, при этом он переходит в состояние Managed. - - - - Managed - - Загруженный из БД или новый, переданный в EntityManager.persist(), экземпляр. Принадлежит некоторому экземпляру EntityManager, другими словами, находится в его персистентном контексте. - Любые изменения экземпляра в состоянии Managed будут сохранены в БД в случае коммита транзакции, к которой принадлежит данный EntityManager - - - - Detached - - Экземпляр, загруженный из БД и отсоединенный от своего персистентного контекста (вследствие закрытия транзакции или сериализации). - Изменения, вносимые в Detached экземпляр, запоминаются в самом этом экземпляре (в полях, добавленных с помощью bytecode enhancement). -Эти изменения будут сохранены в БД, только если данный экземпляр будет снова переведен в состояние Managed путем передачи в метод EntityManager.merge(). - Метод merge() выполняет следующее: загружает из БД экземпляр с тем же идентификатором, переносит в него состояние переданного Detached экземпляра и возвращает загруженный Managed экземпляр. Далее надо работать именно с возвращенным Managed экземпляром. - - - -
-
- Загрузка по требованию - Загрузка по требованию (lazy loading) позволяет загружать связанные сущности отложенно, т.е. только в момент первого обращения к их свойствам. - Загрузка по требованию в сумме порождает больше запросов к БД, чем энергичная загрузка (eager fetching), однако нагрузка при этом растянута во времени. - - Например, при извлечении списка N экземпляров сущности A, содержащих ссылку на экземпляр сущности B, в случае загрузки по требованию будет выполнено N+1 запросов к базе данных. - - - Для минимизации времени отклика и снижения нагрузки необходимо стремиться к меньшему количеству обращений к БД. Для этого в платформе используется механизм представлений, с помощью которого в вышеописанном случае ORM может сформировать один запрос к БД с объединением таблиц. - - - Если A содержит коллекцию B, в случае энергичной загрузки ORM сформирует SQL запрос, возвращающий произведение строк A и B. - - - Иногда загрузка по требованию с точки зрения производительности предпочтительнее, чем энергичная загрузка. Например, когда работает асинхронный процесс, выполняющий некоторую бизнес-логику, общее время выполнения некритично и желательно распределить во времени нагрузку на БД. - - - Загрузка по требованию работает только для экземпляра в состоянии Managed, то есть внутри транзакции, загрузившей данный экземпляр. -
-
- Выполнение JPQL запросов - Для выполнения JPQL запросов предназначен интерфейс Query, ссылку на который который можно получить у текущего экземпляра EntityManager вызовом метода createQuery(). Если запрос предполагается использовать для извлечения сущностей, рекомендуется вызывать createQuery() с передачей типа результата, что приведет к созданию TypedQuery. - Методы Query в основном соответствуют методам стандартного интерфейса - javax.persistence.Query - . Рассмотрим отличия. - - setParameter() - устанавливает значение параметра запроса. При передаче в данный метод экземпляра сущности выполняет неявное преобразование экземпляра в его идентификатор. Например:Customer customer = ...; -TypedQuery<Order> query = entityManager.createQuery( - "select o from sales$Order o where o.customer.id = ?1", Order.class); -query.setParameter(1, customer); - Обратите внимание на сравнение в запросе по идентификатору, но передачу в качестве параметра самого экземпляра сущности. - Вариант метода с передачей implicitConversions = false не выполняет такого преобразования. - - - setView(), addView() - аналогичны одноименным методам интерфейса EntityManager - устанавливают представление, используемое при загрузке данных текущим запросом, не влияя на представление всего EntityManager. - - - getDelegate() - возвращает экземпляр javax.persistence.Query, предоставляемый реализацией ORM. - - -
- Поиск like без учета регистра - Для удобного формирования условия поиска без учета регистра символов и по любой части строки можно использовать префикс (?i) имени параметра запроса, например:select c from sales$Customer c where c.name like :(?i)name - В данном случае ORM переведет значение параметра name в нижний регистр и обрамит символами %, а затем в БД выполнит SQL с условием вида lower(C.NAME) like ? -
-
- Макросы в JPQL - Текст JPQL запроса может включать макросы, которые обрабатываются перед выполнением и превращаются в исполняемый JPQL, дополнительно модифицируя набор параметров. - Макросы, определенные в платформе, решают следующие задачи: - - Позволяют обойти принципиальную невозможность средствами JPQL выразить условие зависимости значения поля от текущего момента времени (не работает арифметика типа current_date-1) - - - Позволяют сравнивать с датой поля типа Timestamp (содержащие дату+время) - - - Рассмотрим их подробно: - - @between - - Имеет вид @between(field_name, moment1, moment2, time_unit), где - - field_name - имя атрибута для сравнения - - - moment1, moment2 - моменты времени, в которые должно попасть значение атрибута field_name. Каждый из моментов должен быть определен выражением с участием переменной now, к которой может быть прибавлено или отнято целое число - - - time_unit - определяет единицу измерения времени, которое прибавляется или вычитается из now в выражениях моментов, а также точность округления моментов. Может быть следующим: year, month, day, hour, minute, second. При включенном базовом проекте workflow можно также использовать единицы рабочего времени: workday, workhour, workminute. - - - Макрос преобразуется в следующее выражение JPQL: field_name >= :moment1 and field_name < :moment2 - Пример 1. Покупатель создан сегодня:select c from sales$Customer where @between(c.createTs, now, now+1, day) - Пример 2. Покупатель создан в течение последних 10 минут:select c from sales$Customer where @between(c.createTs, now-10, now, minute) - Пример 3. Документы, датированные последними 5 рабочими днями (для проектов, включающих workflow): select d from sales$Doc where @between(d.createTs, now-5, now, workday) - - - - @today - - Имеет вид @today(field_name) и обеспечивает формирование условия попадания значения атрибута в текущий день. По сути это частный случай макроса @between. - Пример. - -Пользователь создан сегодня: select d from sales$Doc where @today(d.createTs) - - - - @dateEquals - - Имеет вид @dateEquals(field_name, parameter) и позволяет сформировать условие попадания значения поля field_name типа Timestamp в дату, задаваемую параметром parameter. - Пример:select d from sales$Doc where @dateEquals(d.createTs, :param) - - - - @dateBefore - - Имеет вид @dateBefore(field_name, parameter) и позволяет сформировать условие, что дата значения поля field_name типа Timestamp меньше даты, задаваемой параметром parameter. - Пример:select d from sales$Doc where @dateBefore(d.createTs, :param) - - - - @dateAfter - - Имеет вид @dateAfter(field_name, parameter) и позволяет сформировать условие, что дата значения поля field_name типа Timestamp больше или равна дате, задаваемой параметром parameter. - Пример:select d from sales$Doc where @dateAfter(d.createTs, :param) - - - - Список макросов может быть расширен в прикладном проекте. Для создания нового макроса необходимо определить бин, реализующий интерфейс QueryMacroHandler, и задать ему @Scope("prototype"). Механизм выполнения JPQL запросов создает все доступные бины типа QueryMacroHandler, и по очереди передает им текст запроса с набором параметров. Очередность вызова обработчиков не определена. -
-
-
-
- Управление транзакциями -
- Программное управление транзакциями - Программное управление транзакциями осуществляется с помощью интерфейса com.haulmont.cuba.core.Transaction, ссылку на который можно получить методами createTransaction() или getTransaction() интерфейса инфраструктуры - Persistence - . - Метод createTransaction() создает новую транзакцию и возвращает интерфейс Transaction. Последующие вызовы методов commit(), commitRetaining(), end() этого интерфейса управляют созданной транзакцией. Если в момент создания существовала другая транзакция, то она будет приостановлена, и возобновлена после завершения созданной. - Метод getTransaction() вызывает либо создание новой, либо присоединение к текущей транзакции. Если в момент вызова существовала активная транзакция, то метод успешно завершается, и последующие вызовы commit(), commitRetaining(), end() не оказывают никакого влияния на существующую транзакцию. Однако если end() вызван без предварительного вызова commit(), то текущая транзакция помечается как RollbackOnly. - Пример ручного управления транзакцией:@Inject -private Persistence persistence; -... -Transaction tx = persistence.createTransaction(); -try { - EntityManager em = persistence.getEntityManager(); - Customer customer = new Customer(); - customer.setName("John Smith"); - em.persist(customer); - - tx.commit(); -} finally { - tx.end(); -} - Интерфейс Transaction имеет также метод execute(), принимающий на вход класс-действие, которое нужно выполнить в данной транзакции. Это позволяет организовать управление транзакциями в функциональном стиле, например:persistence.createTransaction().execute(new Transaction.Runnable() { - public void run(EntityManager em) { - // transactional code here - } -}); - Если транзакционный блок должен вернуть результат, класс-действие должен реализовывать интерфейс Transaction.Callable. Если результат не требуется, как в приведенном примере, то класс-действие удобно наследовать от абстрактного класса Transaction.Runnable. - Следует иметь в виду, что метод execute() у некоторого экземпляра Transaction можно вызвать только один раз, так как после выполнения кода класса-действия транзакция завершается. -
-
- Декларативное управление транзакциями - Любой метод управляемого бина Middleware можно пометить аннотацией @org.springframework.transaction.annotation.Transactional, что вызовет автоматическое создание транзакции при вызове этого метода. В таком методе не нужно вызывать Persistence.createTransaction(), а можно сразу получать EntityManager и работать с ним. - Для аннотации @Transactional можно указать параметры. Основным параметром является режим создания транзакции - Propagation. Значение REQUIRED соответствует getTransaction(), значение REQUIRES_NEW - createTransaction(). По умолчанию REQUIRED. - - Декларативное управление транзакциями позволяет уменьшить количество boilerplate кода, однако имеет следующий недостаток: коммит транзакции проиходит вне прикладного кода, что часто затрудняет отладку, т.к. скрывается момент отправки изменений в БД и перехода сущностей в состояние Detached. Кроме того, следует иметь в виду, что декларативная разметка сработает только в случае вызова метода контейнером, т.е. вызов транзакционного метода из другого метода того же самого объекта не приведет к старту транзакции. - - В связи с этим рекомендуется применять декларативное управление транзакциями только для простых случаев типа метода сервиса, читающего некоторый объект и возвращающего его на клиента. -
-
- Примеры взаимодействия транзакций -
- Откат вложенной транзакции - Если вложенная транзакция создана через getTransaction(), то ее откат приведет к невозможности коммита охватывающей транзакции. Например:void methodA() { - Transaction tx = persistence.createTransaction(); - try { - // (1) вызываем метод, создающий вложенную транзакцию - methodB(); - - // (4) в этот момент будет выброшено исключение, т.к. транзакция - // помечена как rollback only - tx.commit(); - } finally { - tx.end(); - } -} - -void methodB() { - Transaction tx = persistence.getTransaction(); - try { - // (2) допустим здесь возникло исключение - tx.commit(); - } catch (Exception e) { - // (3) обрабатываем его и выходим - return; - } finally { - tx.end(); - } -} - Если же транзакция в methodB() будет создана через createTransaction(), то ее откат не окажет никакого влияния на коммит охватывающей транзакции в methodA(). -
-
- Чтение и изменение данных во вложенной транзакции - Рассмотрим сначала зависимую вложенную транзакцию, создаваемую через getTransaction():void methodA() { - Transaction tx = persistence.createTransaction(); - try { - EntityManager em = persistence.getEntityManager(); - - // (1) загружаем сущность, в которой name == "old name" - Employee employee = em.find(Employee.class, id); - assertEquals("old name", employee.getName()); - - // (2) присваиваем новое значение полю - employee.setName("name A"); - - // (3) вызываем метод, создающий вложенную транзакцию - methodB(); - - // (8) здесь происходит коммит изменений в БД, и в ней - // окажется значение "name B" - tx.commit(); - - } finally { - tx.end(); - } -} - -void methodB() { - Transaction tx = persistence.getTransaction(); - try { - // (4) получаем тот же экземпляр EntityManager, что и methodA - EntityManager em = persistence.getEntityManager(); - - // (5) загружаем сущность с тем же идентификатором - Employee employee = em.find(Employee.class, id); - - // (6) значение поля новое, т.к. мы работаем с тем же - // персистентным контекстом, и обращения к БД вообще - // не происходит - assertEquals("name A", employee.getName()); - employee.setName("name B"); - - // (7) в этот момент реально коммита не происходит - tx.commit(); - } finally { - tx.end(); - } -} - Теперь рассмотрим тот же самый пример с независимой вложенной транзакцией, создаваемой через createTransaction(): void methodA() { - Transaction tx = persistence.createTransaction(); - try { - EntityManager em = persistence.getEntityManager(); - - // (1) загружаем сущность, в которой name == "old name" - Employee employee = em.find(Employee.class, id); - assertEquals("old name", employee.getName()); - - // (2) присваиваем новое значение полю - employee.setName("name A"); - - // (3) вызываем метод, создающий вложенную транзакцию - methodB(); - - // (8) здесь возникнет исключение из-за оптимистичной блокировки - // и коммит не пройдет вообще - tx.commit(); - - } finally { - tx.end(); - } -} - -void methodB() { - Transaction tx = persistence.createTransaction(); - try { - // (4) создается новый экземпляр EntityManager, т.к. это - // новая транзакция - EntityManager em = persistence.getEntityManager(); - - // (5) загружаем сущность с тем же идентификатором - Employee employee = em.find(Employee.class, id); - - // (6) значение поля старое, т.к. произошла загрузка из БД - // старого экземпляра сущности - assertEquals("old name", employee.getName()); - - employee.setName("name B"); - - // (7) здесь происходит коммит изменений в БД, и в ней - // окажется значение "name B" - tx.commit(); - - } finally { - tx.end(); - } -} - В последнем случае исключение в точке (8) возникнет только если сущность является оптимистично блокируемой, т.е. если она реализует интерфейс Versioned. -
-
-
- Таймаут транзакции - Для создаваемой транзакции может быть указан таймаут в секундах, при превышении которого транзакция будет прервана и откачена. Таймаут транзакции ограничивает максимальную длительность запросов к базе данных. - При программном управлении транзакциями таймаут включается путем передачи объекта TransactionParams в метод Persistence.createTransaction(). Например:Transaction tx = persistence.createTransaction(new TransactionParams().setTimeout(2)); - При декларативном управлении транзакциями используется параметр timeout аннотации @Transactional, например:@Transactional(timeout = 2) -public void someServiceMethod() { -... - Таймаут по умолчанию может быть задан в свойстве приложения - cuba.defaultQueryTimeoutSec - . -
- Особенности реализации для различных СУБД - PostgreSQL - К сожалению, JDBC драйвер PostgreSQL не поддерживает метод setQueryTimeout() интерфейса java.sql.Statement, поэтому в начале каждой транзакции, для которой определен таймаут (любым способом, включая ненулевое значение свойства - cuba.defaultQueryTimeoutSec - ), выполняется дополнительный оператор в БД: set local statement_timeout to {value}. При этом в случае превышения таймаута запрос будет прерван самим сервером БД. - Для снижения нагрузки от этих дополнительных операторов рекомендуется поступать следующим образом: - - Таймаут по умолчанию устанавливать не на Middleware с помощью свойства cuba.defaultQueryTimeoutSec, в на самом сервере PostgreSQL в файле postgresql.conf, например statement_timeout = 3000 (это в миллисекундах). - - - Для методов, которым требуется большее время таймаута (отчеты и пр.), явно указывать желаемый таймаут в параметрах транзакции. - - - Microsoft SQL Server - Драйвер JTDS поддерживает метод setQueryTimeout() интерфейса java.sql.Statement, поэтому для EntityManager просто устанавливается стандартное свойство javax.persistence.query.timeout, которое соответствующим образом влияет на JDBC запросы. -
-
-
-
- DataService и DataWorker - Управляемый бин DataWorker является универсальным средством для загрузки графов сущностей из базы данных, и для сохранения изменений, произведенных в Detached экземплярах сущностей. Сервис DataService является фасадом для вызова DataWorker с клиентского уровня приложения. - Выделение бизнес-логики загрузки и сохранения в DataWorker дает возможность при необходимости создать свой сервис, делегирующий основную работу DataWorker и выполняющий дополнительные преобразования, например перед возвратом данных на клиентский уровень. - DataWorker всегда стартует новую транзакцию и по завершению работы выполняет коммит, таким образом возвращая сущности в состоянии Detached. - Методы DataWorker: - - load(), loadList() - загружает граф сущностей в сответствии с параметрами переданного объекта LoadContext. - Данные методы проверяют наличие у пользователя права EntityOp.READ на загружаемую сущность. Кроме того, при извлечении сущностей из БД накладываются ограничения групп доступа (см. руководство Платформа CUBA. Подсистема безопасности). Для отмены действия ограничений в текущем запросе можно передать в LoadContext атрибут useSecurityConstraints = false. - - - commit() - сохраняет в базе данных набор сущностей, переданный в объекте CommitContext. Возвращает набор экземпляров сущностей, возвращенных из метода EntityManager.merge(), то есть по сути свежие экземпляры, только что обновленные в БД. Дальнейшая работа должна производиться именно с этими возвращенными экземплярами, чтобы предотвратить потерю данных или исключения оптимистичной блокировки. - Данный метод проверяет наличие у пользователя права EntityOp.UPDATE на изменяемые сущности, и EntityOp.DELETE на удаляемые. - - - commitNotDetached() - аналогичен методу commit(), но предназначен для сохранения в БД экземпляров, у которых отсутствует информация о Detached состоянии. Такие экземпляры сущностей могут быть переданы с клиентов, которые работают не напрямую с объектами, загруженными Middleware, а с дополнительными Data Transfer Objects, либо вообще не с Java объектами, а с их XML или JSON представлением (как например клиенты REST API) - - - В процессе загрузки данных DataWorker может реализовывать дополнительную функциональность, описанную ниже. -
- Запросы с distinct - В JPQL запросах для экранов со списками сущностей, в которых включено постраничное отображение и возможна непредсказуемая модификация запроса универсальным фильтром или механизмом ограничений групп доступа, при отсутствии в запросе оператора distinct может возникать следующий эффект: - - при объединении с коллекцией на уровне извлечения из базы данных возникает набор с дубликатами строк - - - на клиентском уровне в источнике данных дубликаты исчезают, т.к. попадают в мэп (java.util.Map) - - - при постраничном отображении на одной странице оказывается меньшее количество строк чем запрошено, общее количество строк наоборот завышено. - - - Таким образом, рекомендуется в JPQL запросы браузеров включать предложение distinct, которое гарантирует отсутствие дубликатов записей при выборке из базы данных. Однако в некоторых серверах БД (в частности PostgreSQL) при большом количестве извлекаемых записей (более 10000) SQL запрос с distinct выполняется недопустимо долго. - Для решения этой проблемы в платформе реализована возможность корректной работы без distinct на уровне SQL. Данный механизм включается свойством приложения - cuba.inMemoryDistinct - , при активации которого выполняется следующее: - - В JPQL запросе должен по прежнему присутствовать select distinct - - - В DataWorker из JPQL запроса перед отправкой в ORM distinct вырезается - - - После загрузки страницы данных на Middleware удаляются дубликаты и выполняются дополнительные запросы к БД для получения нужного количества строк, которые затем и возвращаются клиенту. - - -
-
- Последовательная выборка - DataWorker может выполнять последовательную выборку данных из результатов предыдущего запроса. Эта возможность используется в универсальном фильтре при последовательном наложении фильтров. - Данный механизм работает следующим образом: - - При получении LoadContext с установленными атрибутами prevQueries и queryKey DataWorker выполняет выборку по предыдущему запросу и сохраняет идентификаторы полученных сущностей в таблице SYS_QUERY_RESULT (соответствующей сущности sys$QueryResult), разделяя наборы записей по идентификаторам пользовательских сессий и ключу сеанса выборки queryKey. - - - Текущий запрос модифицируется для объединения с результатами предыдущего, так что в итоге возвращает данные, соответствующие условиям обоих запросов, объединенных по "И". - - - Далее процесс может повторяться, при этом уменьшающийся набор предыдущих результатов удаляется из таблицы SYS_QUERY_RESULT и заполняется заново. - - - Таблицу SYS_QUERY_RESULT необходимо периодически чистить от ненужных результатов запросов, оставленных завершенными пользовательскими сессиями. Для этого предназначен метод deleteForInactiveSessions бина QueryResultsManagerAPI. В прикладном проекте с включенным параметром - cuba.allowQueryFromSelected - необходимо вызывать этот метод либо из назначенных заданий, либо из стандартного планировщика Spring, например:<task:scheduled-tasks scheduler="scheduler"> - <task:scheduled ref="cuba_QueryResultsManager" method="deleteForInactiveSessions" fixed-rate="600000"/> -</task:scheduled-tasks> -
-
-
- Работа с СУБД -
- Выбор и подключение - Тип используемой СУБД определяется свойством приложения - cuba.dbmsType - и настройкой источника данных. Конфигурационный файл для Tomcat, определяющий источник данных, описан в - - Обратите внимание на применимость СУБД в зависимости от используемых базовых проектов платформы: - - HSQLDB - применяется только для запуска интеграционных тестов платформы, не используется в прикладных проектах - - - PostgreSQL - поддерживается всеми базовыми проектами платформы - - - Microsoft SQL Server - поддерживается базовыми проектами cuba, workflow, fts - - - -
-
- Создание и обновление БД - Проект CUBA-приложения всегда содержит два набора SQL скриптов: - - Скрипты создания БД - предназначены для создания базы данных с нуля. - Скрипты создания располагаются в каталоге /db/init модуля core. Для каждого типа СУБД, поддерживаемой приложением, создается свой набор скриптов и располагается в подкаталоге с именем, соответствующим значению перечисления DbmsType, например /db/init/postgres. Имена скриптов создания должны иметь вид {optional_prefix}create-db.sql. - - - Скрипты обновления БД - предназначены для поэтапного приведения структуры БД к текущему состоянию модели данных. - Скрипты обновления располагаются в каталоге /db/update модуля core. Для каждого типа СУБД, поддерживаемой приложением, создается свой набор скриптов и располагается в подкаталоге с именем, соответствующим значению перечисления DbmsType, например /db/update/postgres. - Скрипты обновления должны иметь имена, которые при сортировке в алфавитном порядке образуют правильную последовательность их выполнения (обычно это хронологическая последовательность их создания). Поэтому рекомендуется задавать имя скрипта обновления в виде {yymmdd}-{description}.sql, где yy - год, mm - месяц, dd - день, description - краткое описание скрипта. Например 121003-addCodeToCategoryAttribute.sql. - Скрипты обновления можно группировать в подкаталоги, главное чтобы путь к скрипту с учетом подкаталога не нарушал хронологической последовательности. Например, можно создавать подкаталоги по номеру года или по году и месяцу. - - - В развернутом приложении скрипты создания и обновления БД располагаются в специальном каталоге скриптов базы данных, задаваемым свойством приложения - cuba.dbDir - . - Скрипты представляют собой текстовые файлы с набором DDL и DML команд, разделенных символом "^". Символ "^" применяется для того, чтобы можно было применять разделитель ";" в составе сложных команд, например при создании функций или триггеров. Встроенный механизм исполнения скриптов разделяет входной файл на команды по разделителю "^" и выполняет каждую команду в отдельной транзакции. Это означает, что при необходимости можно сгруппировать несколько простых операторов (например insert), разделенных точкой с запятой, и обеспечить выполнение их в одной транзакции. -
- Создание БД - Базу данных можно создать двумя способами: с помощью скрипта сборки Gradle или на старте приложения путем запуска автоматического обновления. - Скрипт сборки build.gradle содержит задачу createDb, которая создает указанную в параметрах задачи БД и выполняет на ней все SQL скрипты создания (базовых проектов и самого приложения). - - Если база данных по указанному в скрипте сборки URL существует, она полностью удаляется и создается заново. - - Чтобы инициализировать БД механизмом автоматического обновления (например в развернутом приложении при отсутствии скрипта сборки), нужно выполнить следующее: - - включить свойство приложения - cuba.automaticDatabaseUpdate - - - - создать пустую базу данных, соответствующую URL, заданному в описании источника данных в - context.xml - - - - запустить веб-сервер, содержащий блок Middleware. На старте приложения БД будет проинициализирована и сразу же готова к работе. - - -
-
- Обновление БД - Процесс обновления базы данных заключается в выполнении скриптов обновления, присутствующих в проекте или в каталоге скриптов, которые не зарегистрированы как выполненные в таблице SYS_DB_CHANGELOG. Эти скрипты могут быть выполнены как вручную (используя сторонние инструменты), так и следующими встроенными в систему способами: - - Выполнением задачи updateDb скрипта сборки build.gradle. - - - В работающем приложении: - - на старте Middleware при включенном механизме автоматического обновления - - - после старта вызовом метода updateDatabase() JMX-бина - PersistenceManagerMBean - - - - - -
-
- Механизм автоматического обновления - Механизм автоматического обновления включается свойством приложения - cuba.automaticDatabaseUpdate - . Он активируется на старте блока Middleware до момента полной инициализации приложения, и действует следующим образом: - - Если в БД отсутствует таблица SEC_USER, то считается, что база данных пуста, и запускается полная инициализация с помощью скриптов создания БД. После выполнения инициализирующих скриптов их имена запоминаются в таблице SYS_DB_CHANGELOG. Кроме того, там же сохраняются имена всех доступных скриптов обновления, без их выполнения. - - - Если в БД имеется таблица SEC_USER, но отсутствует таблица SYS_DB_CHANGELOG (это случай, когда в первый раз запускается описываемый механизм на имеющейся рабочей БД), никакие скрипты не запускаются. Вместо этого создается таблица SYS_DB_CHANGELOG и в ней сохраняются имена всех доступных на данный момент скриптов обновления. - - - Если в БД имеются и таблица SEC_USER и таблица SYS_DB_CHANGELOG, производится запуск скриптов обновления, и их имена запоминаются в таблице SYS_DB_CHANGELOG. Причем запускаются только те скрипты, имен которых не было в таблице SYS_DB_CHANGELOG, т.е. не запускавшиеся ранее. -Последовательность запуска скриптов определяется 2-мя факторами: приоритетом базового проекта (см. содержимое каталога скриптов базы данных: 10-cuba, 20-workflow, ...) и именем файла скрипта (с учетом подкаталогов внутри каталога update) в алфавитном порядке. - - -
-
-
- Проектирование БД -
- Соответствие типов данных - В таблице приведено соответствие типов данных, отличающихся для разных СУБД. - - - - - - - - Java - PostgreSQL - MS SQL Server - - - - - UUID - uuid - uniqueidentifier - - - Date - timestamp - datetime - - - Boolean - boolean - tinyint - - - byte[] - bytea - image - - - - -
-
- Особенности MS SQL Server - Microsoft SQL Server использует кластерные индексы для таблиц. - По умолчанию кластерный индекс создается по первичному ключу таблицы, однако используемые в CUBA-приложении ключи типа UUID плохо подходят для кластерного индекса. Поэтому необходимо для каждой таблицы правильно выбрать и создать кластерный индекс. Поле для кластерного индекса должно быть небольшим и монотонно возрастающим, поэтому ориентировочные правила следующие: - - Для большинства таблиц подходит поле CREATE_TS. При этом записи будут физически располагаться в порядке их создания. - - - Для композитных сущностей, если чтение превалирует над записью, имеет смысл использовать ссылку на владельца. При этом записи будут сгруппированы по владельцам, и их извлечение вместе с владельцем будет происходить быстрее. - - - Для небольших (< 100 записей) редко изменяемых таблиц тип кластерного индекса не важен, можно оставить ID. - - - Для таблиц сущностей, унаследованных по стратегии JOINED, в которых нет поля CREATE_TS, нужно создать его искусственно с параметром default current_tmestamp. - - - Пример:create table SALES_CUSTOMER ( - ID uniqueidentifier not null, - CREATE_TS datetime, - ... - primary key nonclustered (ID) -)^ - -create clustered index IDX_SALES_CUSTOMER_CREATE_TS on SALES_CUSTOMER (CREATE_TS)^ - Пример композитной сущности: create table SALES_ITEM ( - ID uniqueidentifier not null, - CREATE_TS datetime, - ... - ORDER_ID uniqueidentifier, - ... - primary key nonclustered (ID), - constraint FK_SALES_ITEM_ORDER foreign key (ORDER_ID) references SALES_ORDER(ID) -)^ - -create clustered index IDX_SALES_ITEM_ORDER on SALES_ITEM (ORDER_ID)^ - Пример унаследованной сущности: create table SALES_DOC ( - CARD_ID uniqueidentifier, - CREATE_TS datetime default current_timestamp, - NUMBER varchar(50), - primary key nonclustered (CARD_ID), - constraint FK_SALES_DOC_CARD foreign key (CARD_ID) references WF_CARD (ID) -)^ - -create clustered index IDX_SALES_DOC_CREATE_TS on SALES_DOC (CREATE_TS)^ - -create index IDX_SALES_DOC_CARD on SALES_DOC (CARD_ID)^ -
-
-
- -
- Универсальный пользовательский интерфейс - Подсистема универсального пользовательского интерфейса (Generic UI, GUI) позволяет разрабатывать экраны пользовательского интерфейса, используя XML и Java. Созданные таким образом экраны одинаково работоспособны в двух -стандартных клиентских блоках: Web Client и Desktop Client. -
- Структура универсального пользовательского интерфейса - - - - - -
- Здесь в центре изображены основные составляющие экранов универсального пользовательского интерфейса: - - XML-дескрипторы - файлы XML, содержащие информацию об источниках данных и компоновке -экрана - - - Контроллеры - классы Java, содержащие логику инициализации экрана -и обработки событий от элементов пользовательского интерфейса. - - - Код экранов приложения, расположенный в модуле gui, взаимодействует с интерфейсами визуальных -компонентов (VCL Interfaces), реализованными по-отдельности в модулях web и desktop базового проекта cuba. Для Web Client реализация основана на фреймворке Vaadin, для Desktop Client – на фреймворке Java Swing. - Библиотека визуальных компонентов (Visual Components Library, VCL) -содержит большой набор готовых компонентов для отображения данных. - Механизм источников данных (Datasources) предоставляет унифицированный -интерфейс, обеспечивающий -функционирование -связанных -с -данными визуальных компонентов. - Инфраструктура клиента (Infrastructure) включает в себя главное окно приложения, -механизмы отображения и взаимодействия экранов UI, а также средства -взаимодействия со средним слоем. -
- Экраны - Экран универсального пользовательского интерфейса состоит из XML-дескриптора и класса контроллера. Дескриптор содержит ссылку на класс контроллера. - Для того, чтобы экран можно было вызывать из главного меню или из Java кода (например из контроллера другого экрана), XML-дескриптор должен быть зарегистрирован в файле - screens.xml - проекта. - Главное меню приложения формируется отдельно для Web Client и Desktop Client на основе файлов - menu.xml - , расположенных соответственно в модулях web и desktop проекта. -
- XML-дескриптор - XML-дескриптор - это файл формата XML, описывающий источники данных и расположение визуальных компонентов экрана. - Схема XML доступна по адресу http://schemas.haulmont.com/cuba/4.0/window.xsd - Рассмотрим структуру дескриптора. - window − корневой элемент. - Атрибуты window: - - - class - − имя класса контроллера - - - messagesPackпакет сообщений данного экрана, который будет использован при получении локализованных строк без указания пакета из XML-дескриптора и из контроллера методом getMessage() - - - caption − заголовок экрана, может содержать ссылку на сообщение из вышеуказанного пакета, например, caption="msg://caption" - - - focusComponent − (необязательно) идентификатор компонента, который получит фокус ввода при отображении экрана. - - - Элементы window: - - metadataContext − опциональный элемент для инициализации представлений (views), необходимых данному экрану. Предпочтительным является определение всех представлений в одном общем файле - views.xml - , так как все описатели представлений разворачиваются в один общий репозиторий, и при рассредоточении описателей по разным файлам трудно обеспечить уникальность имен. - - - dsContext − элемент, определяющий источники данных данного экрана. - - - companions - опциональный элемент, задающий список классов-компаньонов данного контроллера - Элементы companions: - - web - задает компаньон, реализованный в модуле web - - - desktop - задает компаньон, реализованный в модуле desktop - - - Каждый из этих элементов содержит атрибут class, задающий класс компаньона. - - - layout − корневой элемент компоновки экрана. Является сам по себе контейнером с вертикальным расположением компонентов, аналогичным - vbox - . - Атрибуты layout: - - - spacing - - - - - expand - - - - - -
-
- Контроллер экрана - Контроллер экрана - это Java или Groovy класс, связанный с XML-дескриптором, и содержащий логику инициализации и обработки событий экрана. - Контроллер должен быть унаследован от одного из следующих базовых классов: - - - AbstractFrame − реализует интерфейс IFrame и предназначен для реализации фреймов − многократно используемых компонентов экранов. - - - AbstractWindow − реализует интерфейc Window и может быть использован для реализации любых экранов. - - - AbstractLookup − реализует интерфейс Lookup и предназначен для реализации браузеров сущностей с возможностью выбора элемента списка для использования его в вызывающем экране. - - - AbstractEditor − реализует интерфейс Editor и предназначен для реализации экранов редактирования экземпляра сущности. - - - - Если экрану не нужна никакая дополнительная логика, то в качестве контроллера можно использовать сам базовый класс AbstractWindow, AbstractLookup или AbstractEditor, указав его в XML-дескрипторе (эти классы на самом деле не являются абстрактными в смысле невозможности создания экземпляров). Для фрейма класс контроллера можно не указывать вообще. - - Класс контроллера должен быть зарегистрирован в XML-дескрипторе экрана в атрибуте class корневого элемента window. -
- Зависимости контроллеров - В контроллерах можно использовать Dependency Injection для получения ссылок на используемые объекты. Для этого нужно объявить либо поле соответствующего типа, либо метод доступа на запись (setter) с соответствующим типом результата, и добавить ему одну из следующих аннотаций: - - @Inject - простейший вариант, поиск объекта для инжекции будет произведен по типу поля/метода и по имени, эквивалентному имени поля либо имени атрибута (по правилам JavaBeans) для метода - - - @Named("someName") - вариант с явным указанием имени искомого объекта - - - Инжектировать в контроллеры можно следующие объекты: - - Визуальные компоненты данного экрана, определенные в XML-дескрипторе. Если тип атрибута унаследован от Component, в текущем экране будет произведен поиск компонента с соответствующим именем. - - - Действия, определенные в XML-дескрипторе - см. - - - Источники данных, определенные в XML-дескрипторе. Если тип атрибута унаследован от Datasource, в текущем экране будет произведен поиск источника данных с соответствующим именем. - - - UserSession. Если тип атрибута - - UserSession - , будет инжектирован объект текущей пользовательской сессии. - - - DsContext. Если тип атрибута - DsContext, будет инжектирован DsContext текущего экрана. - - - WindowContext. Если тип атрибута - WindowContext, будет инжектирован WindowContext текущего экрана. - - - DataService. Если тип атрибута - com.haulmont.cuba.gui.data.DataService, будет инжектирован этот объект. - - - Любой бин, определенный в контексте данного клиентского блока приложения, в том числе: - - импортируемые клиентом сервисы Middleware - - - ComponentsFactory - - - WindowConfig - - - ExportDisplay - - - - BackgroundWorker - - - - - - Если ничего из вышеперечисленного не подошло и контроллер имеет компаньонов, в случае совпадения типов будет инжектирован компаньон для текущего типа клиента. - - -
-
- Методы контроллеров - Общим методом контроллеров всех типов является метод init(). Этот метод вызывается фреймворком после создания класса окна и всего дерева компонентов, описанного XML-дескриптором. Здесь контроллер может произвести инициализацию экрана перед его открытием (например, создать обработчики нажатий на кнопки). - В метод init() из вызывающего кода передается мэп параметров, которые могут быть использованы внутри контроллера. Эти параметры могут быть переданы как из кода контроллера вызывающего экрана (в методе openWindow()), так и установлены в файле регистрации экранов - screens.xml - . -
-
- AbstractEditor - AbstractEditor − базовый класс контроллеров экранов редактирования экземпляра сущности. - При создании конкретного класса контроллера рекомендуется параметризовать AbstractEditor типом редактируемой сущности. При этом методы getItem и initItem будут работать с конкретным типом сущности и прикладному коду не потребуется дополнительных приведений типов. Например: - public class CustomerEdit extends AbstractEditor<Customer> { -... - @Override - protected void initItem(Customer item) { - ... - Помимо общего для всех контроллеров метода init() в контроллере экрана редактирования можно переопределить следующие: - - - Методы инициализации: - - - initItem - - - postInit - - - - - Методы завершения: - - - postValidate - - - preCommit - - - postCommit - - - - - Диаграммы последовательностей -
- Инициализация экрана - - - - - -
-
- Коммит и закрытие экрана с фреймом editWindowActions - - - - - -
-
- Коммит экрана с фреймом extendedEditWindowActions - - - - - -
-
- Коммит и закрытие экрана с фреймом extendedEditWindowActions - - - - - -
-
-
- Реализация для разных типов клиентов - Базовые классы контроллеров расположены в модуле gui базового проекта cuba и не содержат ссылок на классы реализации визуальных компонентов (Swing или Vaadin), что дает возможность использовать их в клиентах обоих типов. Вместо этого базовые классы контроллеров реализуют дополнительный интерфейс Window.Wrapper и делегируют выполнение "обернутому" окну. - В то же время конкретные классы контроллеров могут быть расположены как в модуле gui, так и в web или desktop, в зависимости от применяемых в проекте клиентских блоков и специфики экрана. Если контроллер является универсальным, но для разных типов клиента требуется дополнительная функциональность, ее можно определить в так называемых классах-компаньонах. - Класс-компаньон располагается в модуле клиента соответствующего типа (web или desktop) и реализует интерфейс, задаваемый в использующем его контроллере. Класс компаньона задается в элементе companions XML-дескриптора экрана. Контроллер может получить ссылку на экземпляр компаньона с помощью инжекции или вызовом getCompanion(), и в нужный момент передать ему управление, например для дополнительной инициализации визуальных компонентов специфичным для данного типа клиента способом. -
-
-
-
- Библиотека визуальных компонентов - Компоненты - Контейнеры -
- Компоненты -
- Диаграмма компонентов - - - - - -
- Component − предок всех визуальных компонентов. Он содержит базовые атрибуты, позволяющие идентифицировать компонент и располагать его на экране. - - - - - - - - Button - - - - - - - - - - - - PopupButton - - - - - - - - - - - - LinkButton - - - - - - - - - - - - Label - - - - - - - - - - - - TextField - - - - - - - - - - - - TextArea - - - - - - - - - - - - DateField - - - - - - - - - - - - TimeField - - - - - - - - - - - - CheckBox - - - - - - - - - - - - PickerField - - - - - - - - - - - - OptionsGroup - - - - - - - - - - - - LookupField - - - - - - - - - - - - LookupPickerField - - - - - - - - - - - - SearchPickerField - - - - - - - - - - - - TwinColumn - - - - - - - - - - - - FileUploadField - - - - - - - - - - - - Table - - - - - - - - - - - - TreeTable - - - - - - - - - - - - GroupTable - - - - - - - - - - - - Tree - - - - - - - - - - - - FieldGroup - - - - - - - - - - - - TokenList - - - - - - - - - - - - GenericFilter - - - - - - - - - - - - -
- Label - Надпись (Label) − текстовый компонент, отображающий статический текст либо значение атрибута сущности. - XML-имя компонента: label -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_label_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент <code>Label</code> реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <figure> - <title>Типы надписей - - - - - -
- Для того чтобы создать компонент надписи, создайте в xml-дескрипторе следующий код: - - Атрибут value предназначен для отображения текста надписи. Обычно значение атрибута задается с помощью ключа пакета сообщений. - Текст, содержащийся в атрибуте value, будет перенесен на следующую строку, если по длине он превысит значение атрибута width. Поэтому если Вам нужно использовать многострочную надпись, укажите требуемое значение атрибута width (применимо только для веб-приложений). Если текст надписи слишком длинный, и значение атрибута width не определено, то текст будет урезан. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="142" contentdepth="24" align="center" fileref="img/gui_label_truncated.png"/> - </imageobject> - </mediaobject> - </figure> - <para>В компоненте надписи есть возможность отображать значение атрибута сущности. Для этого используются атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/label/labelWithDatasource.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибуты <sgmltag>label</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row> - <entry align="left"> - <link linkend="attr_align">align</link> - </entry> - <entry align="left"> - <link linkend="attr_property">property</link> - </entry> - <entry align="left"> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_stylename">stylename</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry align="left"> - <link linkend="attr_value">value</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry align="left"> - <link linkend="attr_visible">visible</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>label</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_formatter">formatter</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_Button"> - <title>Button - Кнопка (Button) − компонент, обеспечивающий выполнение некоторых действий при нажатии на нее пользователем. - XML-имя компонента: button -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_Button_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент кнопки реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Кнопка может содержать текст или пиктограмму (или и то и другое). На рисунке ниже отражены разные виды кнопок.</para> - <figure> - <title>Типы кнопок - - - - - -
- Для создания кнопки с текстом достаточно написать в xml-дескрипторе следующий код: - - Для создания кнопки с пиктограммой: - - Атрибут icon указывает на местоположение пиктограммы. Подробную информацию о том, где следует располагать файлы пиктограмм, Вы можете прочитать в - Для создания кнопки с пользовательским стилем необходимо указать атрибут stylename, а также задать стиль компонента в файле CSS. - - Определение стиля: - - Подробную информацию о том, как создавать пользовательский стиль, Вы можете прочитать в - Основная функция кнопки − выполнить некоторое действие при нажатии на нее. Определить метод контроллера, который будет вызываться при нажатии на кнопку, можно с помощью атрибута invoke. Значением атрибута должно быть имя метода контроллера, вызываемого в ответ на событие нажатия кнопки. Метод должен: - - - Быть public - - - Возвращать void - - - Не иметь аргументов или иметь один аргумент типа Component. Если метод имеет аргумент Component, то при вызове в него будет передан экземпляр вызвавшей кнопки. - - - В качестве примера показано описание кнопки, вызывающей метод lockButton: - - В контроллере экрана необходимо определить метод lockButton: - - Атрибут invoke игнорируется, если для кнопки задан атрибут action. Атрибут action содержит имя Action, соответствующего данной кнопке. - Пример кнопки с атрибутом action: - - Action для кнопки можно создавать программно, в контроллере экрана. - В качестве примера создадим кнопку, имеющую в xml-дескрипторе только атрибут id: - - В контроллере в методе init() экрана определим действие и название для кнопки. - - Атрибуты button: - - c - - - action - - - enable - - - stylename - - - - - align - - - icon - - - visible - - - - - caption - - - id - - - width - - - - - description - - - invoke - - - - -
-
- PopupButton - Кнопка с выпадающим списком действий. - XML-имя компонента: popupButton. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_popupButton_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Кнопка с выпадающим списком действий может содержать текст или пиктограмму (или и то и другое). На рисунке ниже отражены разные виды кнопок.</para> - <figure> - <title>Типы кнопок - - - - - -
- Для создания кнопки с текстом достаточно написать в xml-дескрипторе следующий код: - - Для создания кнопки с пиктограммой: - - Кнопка popupButton содержит выпадающий список действий, которые задаются в элементе actions. -
- Кнопка с выпадающим списком действий - - - - - -
- Атрибуты popupButton: - - c - - - align - - - icon - - - width - - - - - caption - - - id - - - - - description - - - stylename - - - - - enable - - - visible - - - - - Элементы popupButton: - - - - - - - actions - - - - - -
-
- LinkButton - Кнопка-ссылка (LinkButton) − кнопка, выглядящая как гиперссылка. - XML-имя компонента: linkButton -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="70%" align="center" fileref="img/gui_linkButton_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент кнопки-ссылки реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Кнопка-ссылка может содержать текст или пиктограмму (или и то и другое). На рисунке ниже отражены разные виды кнопок.</para> - <figure> - <title>Типы кнопок-ссылок - - - - - -
- Для создания кнопки с текстом достаточно написать в xml-дескрипторе следующий код: - - Для создания кнопки с пиктограммой: - - Для кнопки-ссылки, также как и для обычной кнопки (Button), можно в атрибуте invoke задать метод контроллера, который будет вызываться при нажатии на нее. - В качестве примера показано описание кнопки, вызывающей метод saveActionTest: - - В контроллере экрана необходимо определить метод saveActionTest: - - Атрибуты linkButton: - - c - - - action - - - enable - - - stylename - - - - - align - - - icon - - - visible - - - - - caption - - - id - - - width - - - - - description - - - invoke - - - - -
-
- TextField - Поле для редактирования текста. - XML-имя компонента: textField -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_textField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент текстового поля реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Текстовое поле служит для обеспечения возможности получить текстовую информацию от пользователя, а также для отображения атрибутов <glossterm linkend="entity">сущности</glossterm>.</para> - <para>Для создания простого текстового поля достаточно в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> написать следующий код:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>На рисунке ниже показан вид простого текстового поля.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="180" align="center" fileref="img/gui_textField.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Для создания текстового поля, связанного с данными, необходимо использовать атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldDatasource.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="350" align="center" fileref="img/gui_textField_data.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Если поле не связано с источником данных (то есть не указан источник данных и название атрибута), можно связать текстовое поле с типом данных с помощью атрибута <property id="attr_datatype_1">datatype</property>. Атрибут используется для форматирования значения поля. Значением атрибута <property>datatype</property> является имя типа данных из списка:</para> - <itemizedlist> - <listitem> - <para><literal>boolean</literal></para> - </listitem> - <listitem> - <para><literal>byte array</literal></para> - </listitem> - <listitem> - <para><literal>date</literal></para> - </listitem> - <listitem> - <para><literal>decimal</literal></para> - </listitem> - <listitem> - <para><literal>double</literal></para> - </listitem> - <listitem> - <para><literal>int</literal></para> - </listitem> - <listitem> - <para><literal>long</literal></para> - </listitem> - <listitem> - <para><literal>string</literal></para> - </listitem> - <listitem> - <para><literal>uuid</literal></para> - </listitem> - </itemizedlist> - <para>В качестве примера рассмотрим текстовое поле, связанное с типом данных <literal>Integer</literal>. </para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldInt.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>При переходе из текстового поля, в котором введено значение, не являющееся типом данных <literal>Integer</literal>, приложение выдаст сообщение об ошибке и очистит текстовое поле.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="452" contentdepth="133" align="center" fileref="img/gui_textField_int.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Текстовому полю можно задавать количество строк и столбцов текста с помощью атрибутов <sgmltag id="attr_cols">cols</sgmltag> и <sgmltag id="attr_rows">rows</sgmltag>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldColsRows.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="450" contentdepth="96" align="center" fileref="img/gui_textFieldColsRows.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Вы можете связать текстовое поле с типом данных с помощью атрибута <sgmltag id="attr_datatype">datatype</sgmltag>. Атрибут используется для форматирования значения поля в том случае, если поле не связано с данными (то есть не указан источник данных и название атрибута). Значением атрибута <sgmltag>datatype</sgmltag> является имя типа данных из списка:</para> - <para>Для текстового компонента можно ограничить максимальную длину вводимого текста с помощью атрибута <sgmltag id="attr_maxLength">maxLength</sgmltag>. Значение атрибута устанавливается по умолчанию на основании длины строкового атрибута <glossterm linkend="entity">сущности</glossterm>, если этот параметр указан в аннотации сущности. Значение "-1" означает отсутствие ограничения.</para> - <para>Ниже показан пример, ограничивающий длину вводимого текста в 10 символов.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldMaxLength.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Для компонента <code>TextField</code> существует атрибут <sgmltag id="attr_resizable">resizable</sgmltag>. При задании атрибуту значения <literal>true</literal> и установке количества строк, больших одной, у пользователя появляется возможность изменять размеры компонента.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldResizable.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="330" align="center" fileref="img/gui_textField_resizable.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Для отсечения лишних пробелов в начале и конце значения используется атрибут <sgmltag id="attr_trim">trim</sgmltag>. По умолчанию значение атрибута равно <literal>true</literal>. Пробелы отсекаются в момент получения значения поля с помощью метода доступа.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldTrim.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>В <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана определяем метод, который будет вызываться при нажатии на <link linkend="gui_Button">кнопку</link>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldTrimContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибут <sgmltag id="attr_secret">secret</sgmltag> позволяет преобразовать текстовое поле в поле, которое не показывает символы, введенные пользователем. Вместо этого поле отображает эхо-символы, отличные от введенных. </para> - <para>Текстовое поле с атрибутом <code>secret="true"</code> скрывает введенные данные только в визуальном представлении. При получении значения поля с помощью метода доступа это значение представляется в текстовой форме.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldSecret.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>В <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана определяем метод, который будет вызываться при нажатии на <link linkend="gui_Button">кнопку</link>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldSecretContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_textField_secret.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>textField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_editable">editable</link> - </entry><entry align="left"> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_secret">secret</link> - </entry></row> - <row> - <entry> - <link linkend="attr_cols">cols</link> - </entry> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry> - <link linkend="attr_resizable">resizable</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry> - <link linkend="attr_trim">trim</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_datatype">datatype</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_description">description</link> - </entry> - <entry> - <link linkend="attr_maxLength">maxLength</link> - </entry> - <entry> - <link linkend="attr_rows">rows</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>textField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_formatter">formatter</link> - </entry> - </row> - <row> - <entry> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_TextArea"> - <title>TextArea - Текстовая область (TextArea) − многострочное текстовое поле для редактирования текста. Содержит элементы управления для форматирования текста. - XML-имя компонента: textArea -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="180" align="center" fileref="img/gui_textArea_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент текстовой области реализован только для блока <structname>Web Client</structname>.</para> - <para>К тексту, вводимому в компоненте <code>TextArea</code>, можно применять средства для форматирования: изменять начертание шрифта, его размер, гарнитуру − с помощью элементов управления, расположенных в верхней части компонента.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="473" align="center" fileref="img/gui_textAreaInfo.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>textArea</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_editable">editable</link> - </entry><entry align="left"> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row> - <entry> - <link linkend="attr_cols">cols</link> - </entry> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_rows">rows</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>textArea</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_formatter">formatter</link> - </entry> - </row> - <row> - <entry> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_DateField"> - <title>DateField - Поле для отображения или ввода даты и времени. В самом простом варианте представляет собой поле для отображения даты, справа от него находится кнопка с выпадающим календарем, правее находится поле для ввода времени. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_dateFieldSimple.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Дата и время по умолчанию отображаются в формате, соответствующем выбранной локали.</para> - <para>XML-имя компонента: <sgmltag>dateField</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_dateField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Для создания поля даты, связанного с <link linkend="datasources">источником данных</link>, необходимо определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующим образом:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/dateField/dateField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Возможно изменить формат представления даты и времени с помощью атрибута <sgmltag id="attr_dateFormat">dateFormat</sgmltag> по правилам SimpleDateFormat (<ulink url="http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html">http://docs.oracle.com/javase/6/docs/ api/java/text/SimpleDateFormat.html</ulink>). Значением атрибута может быть либо строка формата, либо ключ в пакете сообщений (если строка начинается с msg://)</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/dateField/dateFieldFormat.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="210" align="center" fileref="img/gui_dateField_format.png"/> - </imageobject> - </mediaobject> - </figure> - <para>В большинстве бизнес-приложений нет необходимости отображать милисекунды или даже время. Точность представления даты или времени контролируется атрибутом <sgmltag id="attr_resolution">resolution</sgmltag>. Значение атрибута должно соответствовать перечислению <code>DateField.Resolution</code> − <literal>SEC</literal>, <literal>MIN</literal>, <literal>HOUR</literal>, <literal>DAY</literal>, <literal>MONTH</literal>, <literal>YEAR</literal>. По умолчанию точность определяется по аннотации <code>javax.persistence.Temporal</code> соответствующего атрибута <glossterm linkend="entity">сущности</glossterm>.</para> - <para>Если <code>resolution="DAY"</code> и не указан атрибут <sgmltag>dateFormat</sgmltag>, то в качестве формата будет взят формат, указанный в <link linkend="main_message_pack">главном пакете сообщений</link> с ключом <sgmltag>dateFormat</sgmltag>.</para> - <para>Если <code>resolution="MIN"</code> и не указан атрибут <sgmltag>dateFormat</sgmltag>, то в качестве формата будет взят формат, указанный в <link linkend="main_message_pack">главном пакете сообщений</link> с ключом <sgmltag>dateTimeFormat</sgmltag>.</para> - <para>Ниже показано определения поля для ввода даты с точностью до месяца.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/dateField/dateFieldResolution.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="350" align="center" fileref="img/gui_dateField_resolution.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>dateField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_editable">editable</link> - </entry><entry align="left"> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row> - <entry> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_dateFormat">dateFormat</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_resolution">resolution</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>dateField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_TimeField"> - <title>TimeField - Поле для отображения или ввода времени. В самом простом варианте представляет собой поле для отображения или ввода времени в часах и минутах. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="142" align="center" fileref="img/gui_timeField.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>timeField</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_timeField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Для создания поля времени, связанного с <link linkend="datasources">источником данных</link>, необходимо указать атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link> в определении поля:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/timeField/timeFieldDs.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Для создания простого поля для ввода времени необходимо определить его в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующим образом:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/timeField/timeField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>С помощью установки атрибута <sgmltag id="attr_showSeconds">showSeconds</sgmltag> в значение <literal>true</literal> можно настроить отображение секунд.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/timeField/timeFieldSec.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="235" align="center" fileref="img/gui_timeFieldSec.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>timeField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_enable">enable</link> - </entry><entry align="left"> - <link linkend="attr_required">required</link> - </entry><entry> - <link linkend="attr_visible">visible</link> - </entry></row> - <row> - <entry> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_showSeconds">showSeconds</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_editable">editable</link> - </entry> - <entry align="left"> - <link linkend="attr_property">property</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>timeField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_FieldGroup"> - <title>FieldGroup - Группа полей (FieldGroup) генерализует представление атрибутов одного или более источника данных. Представление поля зависит от типа соответствующего атрибута. Для отображения сущностей может использоваться как LookupField, так и PickerField. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_fieldGroup.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>fieldGroup</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_FieldGroup_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Ниже представлено описание группы полей в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> экрана:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/fieldGroup/fieldGroup.txt" encoding="UTF-8" parse="text"/></programlisting> - <para><link linkend="datasources">Источник данных</link> (атрибут <link linkend="attr_datasource" id="attr_fG_datasource">datasource</link>) можно задать как для всего компонента целиком, так и для каждого поля в отдельности. Если для компонента <sgmltag>fieldGroup</sgmltag> атрибут не задан, то атрибут <sgmltag>datasource</sgmltag> должен быть задан для каждого <link linkend="attr_field">поля</link> в отдельности, либо представление полей должно задаваться разработчиком.</para> - <para>При установке атрибута <sgmltag id="attr_collapsable">collapsable</sgmltag> в значение <literal>true</literal> компонент может сворачивать свое содержимое.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_fieldGroup_collapsable.png"/> - </imageobject> - </mediaobject> - </figure> - <para id="attr_captionAlignment"><sgmltag>captionAlignment</sgmltag> − необязательный атрибут, задает позицию отображения заголовков полей. Может принимать два значения: <literal>LEFT</literal>, <literal>TOP</literal>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_FieldGroup_captionAlignment.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент <sgmltag>fieldGroup</sgmltag> состоит из списков <link linkend="attr_field">полей</link>, сгруппированных в элементе <sgmltag id="attr_fG_column">column</sgmltag>. </para> - <itemizedlist> - <listitem> - <para>С помощью атрибута <sgmltag id="attr_fG_width">width</sgmltag> задается ширину всех полей (без учета заголовка) в колонке. Это значение может быть перекрыто в каждом отдельном поле.</para> - </listitem> - <listitem> - <para>Атрибут <sgmltag id="attr_fG_flex">flex</sgmltag> определяет соотношение ширин колонок.</para> - </listitem> - </itemizedlist> - <para>Элементами <link linkend="attr_fG_column">column</link> служат поля (<property>field</property>).</para> - <para id="attr_field"><sgmltag>field</sgmltag> − элемент, содержащий описание выводимого поля. Может содержать описание формата выводимого значения (<link linkend="element_formatter">formatter</link>) и описание валидаторов, применяемых к соответствующему полю (<link linkend="element_validator">validator</link>). Рассмотрим более подробно некоторые атрибуты элемента <sgmltag>field</sgmltag>. </para> - <itemizedlist> - <listitem> - <para><link linkend="attr_id" id="attr_field_id">id</link> − обязательный атрибут, идентификатор поля. Имеет физический смысл: в случае, если задан <link linkend="datasources">источник данных</link>, то идентификатором поля должно быть имя атрибута, иначе − просто идентификатор поля.</para> - </listitem> - <listitem> - <para><link linkend="attr_caption" id="attr_field_caption">caption</link> − необязательный атрибут, содержащий заголовок поля.</para> - </listitem> - <listitem> - <para><link linkend="attr_visible" id="attr_field_visible">visible</link> − необязательный атрибут, отвечает за сокрытие поля.</para> - </listitem> - <listitem> - <para><link linkend="attr_optionsDatasource" id="attr_field_optionsDatasource">optionsDatasource</link> − необязательный атрибут, определяющий список возможных значений поля.</para> - </listitem> - <listitem> - <para><link linkend="attr_datasource" id="attr_field_datasource">datasource</link> − необязательный атрибут, содержит имя <link linkend="datasources">источника данных</link> для конкретного поля.</para> - </listitem> - <listitem> - <para>Есть возможность самостоятельно задать представление поля с помощью атрибута <sgmltag id="attr_custom">custom</sgmltag>. Значение атрибута в этом случае должно быть равно <literal>true</literal>. Представление пользовательского поля необходимо определить в контроллере экрана. Компонент представления должен реализовывать интерфейс <code>Field</code>. Ниже представлен пример представления пользовательского поля:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/fieldGroup/fieldGroupCustom.txt" encoding="UTF-8" parse="text"/></programlisting> - </listitem> - <listitem> - <para><link linkend="attr_width" id="attr_field_width">width</link> − необязательный атрибут, содержит ширину поля (без учета заголовка)</para> - </listitem> - <listitem> - <para>Если значение атрибута <sgmltag id="attr_field_rows">rows</sgmltag> больше <literal>1</literal>, то в ячейке будет компонент <link linkend="gui_TextArea">TextArea</link>.</para> - </listitem> - </itemizedlist> - <para>Атрибуты <sgmltag>fieldGroup</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left">border</entry>editable<entry align="left"> - <link linkend="attr_fG_datasource">datasource</link> - </entry><entry align="left"> - <link linkend="attr_id">id</link> - </entry><entry/></row> - <row> - <entry> - <link linkend="attr_caption">caption</link> - </entry> - <entry align="left"> - <link linkend="attr_editable">editable</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_captionAlignment">captionAlignment</link> - </entry> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_collapsable">collapsable</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>fieldGroup</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry> - <link linkend="element_fG_field">field</link> - </entry> - </row> - <row> - <entry> - <link linkend="element_fG_column">column</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - <para>Атрибуты <link linkend="attr_field" id="element_fG_field">field</link>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_field_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_dateFormat">dateFormat</link> - </entry><entry align="left"> - <link linkend="attr_field_id">id</link> - </entry><entry> - <link linkend="attr_field_rows">rows</link> - </entry></row> - <row> - <entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry> - <link linkend="attr_maxLength">maxLength</link> - </entry> - <entry> - <link linkend="attr_showSeconds">showSeconds</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_clickAction">clickAction</link> - </entry> - <entry align="left"> - <sgmltag>descriptionProperty</sgmltag> - </entry> - <entry> - <link linkend="attr_field_optionsDatasource">optionsDatasource</link> - </entry> - <entry> - <link linkend="attr_field_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_cols">cols</link> - </entry> - <entry align="left"> - <link linkend="attr_editable">editable</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry> - <link linkend="attr_field_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_custom">custom</link> - </entry> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_field_datasource">datasource</link> - </entry> - <entry align="left">field</entry> - <entry> - <link linkend="attr_resolution">resolution</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты <link linkend="attr_fG_column" id="element_fG_column">column</link>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_fG_flex">flex</link> - </entry>editable</row> - <row> - <entry> - <link linkend="attr_fG_width">width</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_CheckBox"> - <title>CheckBox - Флажок (CheckBox) − компонент, имеющий два состояния: выбран, не выбран. Флажки используются -там, где нужно предоставить пользователю возможность что-то включить или -выключить. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="170" align="center" fileref="img/CheckBox.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>checkBox</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="70%" align="center" fileref="img/gui_checkBox_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Для создания флажка достаточно в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> написать следующий код:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/checkBox/checkBox.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Сброс или установка флажка изменяет его состояние. Состояние − это свойство типа <code>Boolean</code>, может быть получено с помощью метода <code>getValue()</code> и установлено с помощью метода <code>setValue()</code>. </para> - <para>При каждом изменении состояния флажка генерируется событие элемента, которое может быть обработано с помощью <code>ValueListener</code>.</para> - <para>Ниже приведен код <glossterm linkend="screen_controller_glossentry">контроллера</glossterm>, демонстрирующий работу с флажком. При установке флажка на экране возникает сообщение <quote>Разрешить создание нового документа</quote>, при сбросе флажка − <quote>Не разрешать создание нового документа</quote>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/checkBox/checkBoxContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибуты <sgmltag>checkBox</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_align">align</link> - </entry>editable<entry align="left"> - <link linkend="attr_editable">editable</link> - </entry><entry align="left"> - <link linkend="attr_property">property</link> - </entry><entry/></row> - <row> - <entry> - <link linkend="attr_caption">caption</link> - </entry> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>checkBox</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_PickerField"> - <title>PickerField - Поле ввода с дополнительными кнопками действий (PickerField) позволяет отображать экземпляр сущности в текстовом поле и выполнять действия нажатием на кнопки справа. - XML-имя компонента: pickerField. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_pickerField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Для того чтобы создать компонент <code>PickerField</code>, создайте в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующий код:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldMetaClass.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибут <sgmltag id="attr_metaclass">metaClass</sgmltag> задает тип <glossterm linkend="entity">сущности</glossterm>, экземпляр которой будет выбираться с помощью действия поиска (<literal>lookup</literal>). Для заданной сущности должен быть определен экран <literal>Название_сущности.lookup</literal> либо в атрибуте <sgmltag>lookupScreen</sgmltag> указан идентификатор экрана, который нужно открыть для выбора экземпляра сущности.</para> - <para>Определить компонент можно также следующим образом:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldProperty.txt" encoding="UTF-8" parse="text"/></programlisting> - <warning> - <para>Для правильной работы компонента <code>PickerField</code> необходима установка атрибута <link linkend="attr_metaclass">metaClass</link> либо одновременная установка атрибутов <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>.</para> - </warning> - <caution> - <title>Подсказка - Если при объявлении компонента никаких действий не задано, загрузчик XML определит для него действия lookup и clear. - - Поэтому если требуются все стандартные действия, их нужно определить с помощью элемента actions. actions − необязательный элемент, определяющий набор действий (action) (кнопок справа). В элементе action можно описывать как стандартные действия, так и произвольные. - Стандартные действия определяются перечислением PickerField.ActionType: lookup, clear, open. - -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="326" align="center" fileref="img/gui_pickerFieldActionsSt.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Ниже показано описание <code>PickerField</code>, имеющего произвольные действия.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldUserActions.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Действия необходимо определить в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldUserActionsContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="286" align="center" fileref="img/gui_pickerField_custom.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Для компонента определены горячие клавиши. Для стандартных действий:</para> - <itemizedlist> - <listitem> - <para><code>Lookup</code> − <keycombo> - <keycap>CTRL</keycap> - <keycap>ALT</keycap> - <keycap>L</keycap> - </keycombo></para> - </listitem> - <listitem> - <para><code>Open</code> − <keycombo> - <keycap>CTRL</keycap> - <keycap>ALT</keycap> - <keycap>O</keycap> - </keycombo></para> - </listitem> - <listitem> - <para><code>Clear</code> − <keycombo> - <keycap>CTRL</keycap> - <keycap>ALT</keycap> - <keycap>C</keycap> - </keycombo></para> - </listitem> - </itemizedlist> - <para>Чтобы добавить пользовательскую горячую клавишу, необходимо чтобы класс <link linkend="gui_Action">действия</link> реализовывал интерфейс <code>com.haulmont.cuba.gui.components.ShortcutAction</code></para> - <para>Также поддерживается вызов действий сочетанием <keycombo> - <keycap>CTRL</keycap> - <keycap>ALT</keycap> - <keycap>1</keycap> - </keycombo>, <keycombo> - <keycap>CTRL</keycap> - <keycap>ALT</keycap> - <keycap>2</keycap> - </keycombo> и так далее.</para> - <para>Сочетания клавиш можно переназначить в ClientConfig</para> - <itemizedlist> - <listitem> - <para><property>cuba.gui.pickerShortcut.modifiers</property> − модификаторы для вызова <link linkend="gui_Action">действий</link> по порядковому номеру</para> - </listitem> - <listitem> - <para><property>cuba.gui.pickerShortcut.lookup</property> − Lookup Action</para> - </listitem> - <listitem> - <para><property>cuba.gui.pickerShortcut.open</property> − Open Action</para> - </listitem> - <listitem> - <para><property>cuba.gui.pickerShortcut.clear</property> − Clear Action</para> - </listitem> - </itemizedlist> - <para>Атрибуты <sgmltag>pickerField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_enable">enable</link> - </entry><entry align="left"> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_width">width</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_height">height</link> - </entry>required<entry> - <link linkend="attr_required">required</link> - </entry><entry/></row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_lookupScreen">lookupScreen</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry> - <link linkend="attr_metaclass">metaClass</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_OptionsGroup"> - <title>OptionsGroup - Компонент, который обеспечивает выбор из альтернатив, используя группу переключателей для выбора единственного значения из списка или используя группу флажков для выбора нескольких значений из списка. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="265" align="center" fileref="img/gui_optionsGroup.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>optionsGroup</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_OptionsGroup_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Если в качестве значений списка используются экземпляры <glossterm linkend="entity">сущностей</glossterm>, нужно использовать атрибут <link linkend="attr_optionsDatasource">optionsDatasource</link> при описании компонента.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/optionsGroup/optionsGroupOptDs.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Если в качестве значений списка используются, например, значения перечисления (enum), то следует использовать атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>. </para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/optionsGroup/optionsGroupDatasource.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Значения списка можно создать программно, в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/optionsGroup/optionsGroup.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибут <sgmltag id="attr_orientation">orientation</sgmltag> задает расположение элементов группы. По умолчанию элементы располагаются по вертикали. Значение <literal>"horizontal"</literal> служит для горизонтального расположения.</para> - <para>Для задания режима выбора значений служит атрибут <sgmltag id="attr_multiselect">multiselect</sgmltag>. Если <code>"multiselect=false"</code>, то компонент будет представлен как группа переключателей, иначе − как группа флажков. По умолчанию значение атрибута равно <literal>false</literal>.</para> - <para>Атрибуты <sgmltag>optionsGroup</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_enable">enable</link> - </entry><entry align="left"> - <link linkend="attr_orientation">orientation</link> - </entry><entry> - <link linkend="attr_visible">visible</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_height">height</link> - </entry>required<entry> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_width">width</link> - </entry></row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_multiselect">multiselect</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>optionsGroup</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_LookupField"> - <title>LookupField - Компонент, реализующий раскрывающийся список. Раскрывающийся список служит для выбора одного из множества -доступных вариантов. В составе такого списка присутствует активизирующая его кнопка и поле редактирования. Раскрывающийся список реализует фильтрацию значений в реальном времени (по мере ввода значения пользователем) и постраничный вывод доступных значений. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="165" align="center" fileref="img/gui_lookupField.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>lookupField</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_LookupField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Если в качестве значений списка используются экземпляры <glossterm linkend="entity">сущностей</glossterm>, нужно использовать атрибут <link linkend="attr_optionsDatasource">optionsDatasource</link> при описании компонента.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/lookupField/lookupFieldOptDs.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Ниже представлено описание раскрывающегося списка, значения которого заданы с помощью установки <link linkend="datasources">источника данных</link> и имени атрибута <glossterm linkend="entity">сущности</glossterm>. Атрибут сущности является перечислением (enum).</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/lookupField/lookupField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>С помощью атрибута <sgmltag id="attr_filterMode">filterMode</sgmltag> можно задать тип фильтрации значений:</para> - <itemizedlist> - <listitem> - <para><literal>NO</literal> − нет фильтрации</para> - </listitem> - <listitem> - <para><literal>STARTS_WITH</literal> − по началу фразы</para> - </listitem> - <listitem> - <para><literal>CONTAINS</literal> − по любому вхождению</para> - </listitem> - </itemizedlist> - <para>Атрибуты <sgmltag>lookupField</sgmltag>: </para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_enable">enable</link> - </entry><entry align="left"> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry><entry> - <link linkend="attr_visible">visible</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_filterMode">filterMode</link> - </entry>required<entry> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_width">width</link> - </entry></row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry>nullName</entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>lookupField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_LookupPickerField"> - <title>LookupPickerField - Раскрывающийся список с расширенной функциональностью (LookupPickerField) позволяет отображать экземпляр сущности в текстовом поле, выбирать экземпляр в раскрывающемся списке и выполнять действия нажатием на кнопки справа. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="260" align="center" fileref="img/gui_lookupPickerField.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>lookupPickerField</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_LookupPickerField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Ниже представлено описание раскрывающегося списка с расширенной функциональностью.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/lookupPickerField/lookupPickerField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибуты <sgmltag>lookupPickerField</sgmltag>: </para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_enable">enable</link> - </entry><entry align="left">nullName</entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_filterMode">filterMode</link> - </entry>required<entry> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry><entry> - <link linkend="attr_visible">visible</link> - </entry></row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_property">property</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry/> - </row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry> - <link linkend="attr_metaclass">metaClass</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>lookupPickerField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_validator">validator</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_action">actions</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_SearchPickerField"> - <title>SearchPickerField - Поле поиска с расширенной функциональностью (SearchPickerField) служит для поиска значения по заранее определенному условию. Для работы с полем достаточно ввести хотя бы один символ и нажать клавишу Enter. Если будут найдены несколько совпадений, значения будут отображены в виде списка. С помощью дополнительных кнопок можно открывать экземпляр выбранной в поле сущности, удалять экземпляр или выбирать экземпляр из экрана списка сущностей. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="308" contentdepth="96" align="center" fileref="img/gui_searchPickerFieldOverlap.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>searchPickerField</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="90%" align="center" fileref="img/gui_SearchPickerField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован только для блока <structname>Web Client</structname>.</para> - <para>Для компонента <sgmltag>searchPickerField</sgmltag> необходимо предварительно создать <link linkend="datasources">источник данных</link>, предназначенный для работы с коллекцией <glossterm linkend="entity">сущностей</glossterm>, и задать в нем запрос, содержащий условия поиска. Например:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchPickerField/searchPickerField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>При описании компонента в <glossterm linkend="screen_xml_glossentry">XML-дескрипторе</glossterm> используйте атрибут <link linkend="attr_optionsDatasource">optionsDatasource</link>. </para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchPickerField/searchPickerField.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Значение атрибута <sgmltag id="minSearchStringLength_attr_1">minSearchStringLength</sgmltag> − это минимальное количество символов, необходимых для поиска значения.</para> - <para>В <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана можно указать содержание нотификаций, которые будут выводиться на экран в двух случаях:</para> - <itemizedlist> - <listitem> - <para>если количество введенных символов меньше значения атрибута <link linkend="minSearchStringLength_attr">minSearchStringLength</link></para> - </listitem> - <listitem> - <para>если не найдено совпадений существующих в источнике данных значений и введенных символов.</para> - </listitem> - </itemizedlist> - <para>Пример задания нотификаций в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchField/searchFieldContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Если при объявлении компонента никаких <link linkend="gui_Action">действий</link> не задано, загрузчик XML определит для него действия <literal>lookup</literal> и <literal>open</literal>.</para> - <para>Стандартные действия определяются перечислением <code>PickerField.ActionType</code>: <literal>lookup</literal>, <literal>clear</literal>, <literal>open</literal>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchPickerField/searchPickerFieldActions.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="257" contentdepth="97" align="center" fileref="img/gui_searchPickerFieldAllActions.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>searchPickerField</sgmltag>: </para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_enable">enable</link> - </entry><entry align="left"> - <link linkend="minSearchStringLength_attr">minSearchStringLength</link> - </entry><entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_filterMode">filterMode</link> - </entry>required<entry>nullName</entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_property">property</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry> - <link linkend="attr_metaclass">metaClass</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>searchPickerField</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry> - <link linkend="gui_Action">actions</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - </section> - <section id="gui_TwinColumn"> - <title>TwinColumn - Сдвоенный список (TwinColumn) представляет собой компонент множественного выбора, имеющий два находящихся рядом списка: доступных и выбранных опций. В левом списке содержатся доступные невыбранные значения, в правом списке содержатся выбранные значения. Пользователь может выбрать значения из левого списка и нажать на кнопку - - - - , переместив их таким образом в правый список. Значения могут быть отменены для выбора путем выделения их в правом списке и нажатия на кнопку - - - - . Для каждого значения можно задать уникальный стиль отображения и пиктограмму. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="235" align="center" fileref="img/TwinColumn.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>twinColumn</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="173" contentdepth="389" align="center" fileref="img/gui_TwinColumn_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован только для блока <structname>Web Client</structname>.</para> - <para>Ниже представлено описание сдвоенного списка в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/twinColumn/twinColumnOptDs.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>В данном примере используется атрибут <sgmltag id="attr_columns_twin">columns</sgmltag> для задания количества колонок текста в списке и атрибут <sgmltag id="attr_rows_twin">rows</sgmltag> для задания количества строк текста в списке.</para> - <para>Атрибуты <sgmltag>twinColumn</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_editable">editable</link> - </entry><entry align="left"> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_enable">enable</link> - </entry>required<entry> - <link linkend="attr_property">property</link> - </entry><entry> - <link linkend="attr_visible">visible</link> - </entry></row> - <row> - <entry align="left"> - <link linkend="attr_columns_twin">columns</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - <row> - <entry> - <link linkend="attr_description">description</link> - </entry> - <entry>nullName</entry> - <entry> - <link linkend="attr_rows">rows</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>twinColumn</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_validator">validator</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - <para>Для задания внешнего вида опций нужно реализовать интерфейс <code>com.haulmont.cuba.gui.components.StyleProvider</code> как показано в следующем примере:</para> - <programlisting>TwinColumn twinColumn = getComponent("groups"); - twinColumn.setStyleProvider(new TwinColumn.StyleProvider() { - public String getStyleName(Entity item, Object property, boolean selected) { - if (((Group) item).getName().equals("Company")) { - return "companyStyle"; - } else { - return null; - } - } - - public String getItemIcon(Entity item, boolean selected) { - if (((Group) item).getName().equals("Company")) { - return "theme:icons/company.png"; - } else { - return null; - } - } - });</programlisting> - </section> - <section id="gui_TokenList"> - <title>TokenList - Список маркеров (TokenList) представляет собой компонент, имеющий упрощенный вариант отображения и формирования списка сущностей. - Для выбора значений используется компонент, подобный LookupPickerField -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="310" align="center" fileref="img/gui_tokenList.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>tokenList</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_TokenList_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> - <para>Для создания списка маркеров необходимо задать описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tokenList/tokenList.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>С помощью атрибута <sgmltag id="attr_position">position</sgmltag> можно задавать позиционирование раскрывающегося списка. Атрибут может принимать два значения:</para> - <itemizedlist> - <listitem> - <para><literal>TOP</literal></para> - </listitem> - <listitem> - <para><literal>BOTTOM</literal></para> - </listitem> - </itemizedlist> - <figure> - <title>Список маркеров со значением атрибута position="BOTTOM" - - - - - -
- Атрибут inline задает отображение списка выбранных значений: вертикально или горизонтально. Значение true соответствует горизонтальному расположению, значение false − вертикальному. -
- Список маркеров с горизонтальным расположением выбранных значений - - - - - -
- Установка атрибута simple в значение true позволяет сворачивать компонент, оставляя только кнопку добавления. При нажатии на кнопку добавления сразу показывается экран списка экземпляров сущности, для которой задан источник данных. -
- Список маркеров со значением атрибута simple="true" - - - - - -
- Элемент tokenList должен содержать следующие элементы: - - - lookup − описатель компонента выбора значений. - Атрибут lookup задает вид списка. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_tokenListLookup.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибут <sgmltag id="attr_lookupScreen">lookupScreen</sgmltag> задает идентификатор экрана для выбора значений в режиме <link linkend="attr_lookup">lookup</link>.</para> - <para>С помощью атрибута <sgmltag id="attr_openType">openType</sgmltag> можно задать способ открытия окна списка экземпляров сущности:</para> - <itemizedlist> - <listitem> - <para><literal>NEW_TAB</literal> − открытие экрана в новой вкладке</para> - </listitem> - <listitem> - <para><literal>DIALOG</literal> − открытие экрана в диалоговом окне</para> - </listitem> - <listitem> - <para><literal>NEW_WINDOW</literal> − открытие экрана в новом окне</para> - </listitem> - <listitem> - <para><literal>THIS_TAB</literal> − открытие экрана в текущей вкладке</para> - </listitem> - </itemizedlist> - </listitem> - <listitem> - <para id="element_button"><sgmltag>button</sgmltag> − описатель кнопки добавления значений</para> - </listitem> - </itemizedlist> - <para>Ниже расположен пример <glossterm linkend="screen_controller_glossentry">контроллера</glossterm>, в котором задаются обработчики добавления нового значения и удаления значения:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tokenList/tokenListContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибуты <sgmltag>tokenList</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_editable">editable</link> - </entry><entry align="left"> - <link linkend="attr_inline">inline</link> - </entry><entry> - <link linkend="attr_visible">visible</link> - </entry></row> - <row><entry> - <link linkend="attr_captionProperty">captionProperty</link> - </entry><entry align="left"> - <link linkend="attr_enable">enable</link> - </entry>required<entry> - <link linkend="attr_position">position</link> - </entry><entry> - <link linkend="attr_width">width</link> - </entry></row> - <row> - <entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_simple">simple</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>tokenList</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry align="left"> - <link linkend="element_tL_lookup">lookup</link> - </entry> - </row> - <row> - <entry> - <link linkend="element_tL_button">button</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - <para>Атрибуты <link linkend="element_lookup" id="element_tL_lookup">lookup</link>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_captionProperty">captionProperty</link> - </entry>editable<entry align="left"> - <link linkend="attr_lookupScreen">lookupScreen</link> - </entry><entry align="left"> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry><entry/></row> - <row><entry> - <link linkend="attr_filterMode">filterMode</link> - </entry><entry align="left"> - <sgmltag>multiselect</sgmltag> - </entry>required<entry/><entry/></row> - <row> - <entry align="left"> - <link linkend="attr_lookup">lookup</link> - </entry> - <entry align="left"> - <link linkend="attr_openType">openType</link> - </entry> - <entry/> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты <link linkend="element_button" id="element_tL_button">button</link>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable</row> - <row><entry> - <link linkend="attr_icon">icon</link> - </entry>required</row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_Table"> - <title>Table - Таблица (Table) дает возможность выводить двухмерную информацию, расположенную в виде строк и столбцов, настраивать и сортировать данные, управлять заголовками таблицы и ее выделенными элементами. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="300" align="center" fileref="img/gui_table.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>table</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="90%" align="center" fileref="img/gui_Table_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Интерфейс <code>Table</code>, наследуется от интерфейса <code>ListComponent</code> − базового интерфейса компонентов, предназначенных для работы с коллекциями <glossterm linkend="entity">сущностей</glossterm>.</para> - <para>Для того чтобы создать простую таблицу, необходимо в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> задать <link linkend="datasources">источник данных</link> и определить описание таблицы:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/table.txt" encoding="UTF-8" parse="text"/></programlisting> - <warning> - <para>Обязательными для таблицы являются элементы <link linkend="element_table_columns">columns</link> и <link linkend="element_table_rows">rows</link>.</para> - </warning> - <para>Атрибут <sgmltag id="attr_presentations">presentations</sgmltag> управляет механизмом <link linkend="gui_Table_presentations">представлений</link>. Значение по умолчанию равно <literal>false</literal>. Когда значение атрибута равно <literal>true</literal>, то в верхнем правом углу таблицы появляется значок <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_presentation.png"/> - </imageobject> - </inlinemediaobject>.</para> - <para>Атрибут <sgmltag id="attr_sortable">sortable</sgmltag> разрешает или запрещает сортировку в таблице. По умолчанию имеет значение <literal>true</literal>. Если сортировка разрешена, то при нажатии на название колонки справа от названия появляется значок <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_sortable_down.png"/> - </imageobject> - </inlinemediaobject>/<inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_sortable_up.png"/> - </imageobject> - </inlinemediaobject>.</para> - <para>Существует возможность использования для колонок функций <link linkend="gui_Table_aggregation">агрегирования</link> без модификации <link linkend="datasources">источника данных</link> с помощью атрибута <sgmltag id="attr_aggregatable">aggregatable</sgmltag>. По умолчанию атрибут имеет значение <literal>false</literal>.</para> - <para>Атрибут <sgmltag id="attr_showTotalAggregation">showTotalAggregation</sgmltag> разрешает или запрещает отображение строки итоговой агрегации в таблице. По умолчанию имеет значение <literal>true</literal>.</para> - <para>Можно задать множественное выделение строк в таблице с помощью присваивания атрибуту <sgmltag id="attr_table_multiselect">multiselect</sgmltag> значения <literal>true</literal>. По умолчанию значение атрибута равно <literal>false</literal>.</para> - <para>С помощью элемента <sgmltag id="element_table_actions">actions</sgmltag> возможно определять набор действий (<link linkend="gui_Action">action</link>). Кроме описания произвольного действия возможно использование стандартных действий, определяемых перечислением <code>ListActionType</code>: <code>create</code>, <code>edit</code>, <code>remove</code>, <code>refresh</code>, <code>add</code>, <code>exclude</code>, <code>excel</code>.</para> - <para><sgmltag id="attr_rowsCount">rowsCount</sgmltag> − необязательный элемент, создающий для таблицы компонент <code>RowsCount</code>, который позволяет загружать в таблицу данные постранично.</para> - <para>Элемент <sgmltag id="element_table_rows">rows</sgmltag> является обязательным. В этом элементе необходимо объявить используемый <link linkend="datasources">источник данных</link>.</para> - <para>Атрибут <sgmltag id="attr_headerMode">headerMode</sgmltag> элемента <sgmltag>rows</sgmltag> задает вариант отображения заголовков рядов:</para> - <itemizedlist> - <listitem> - <para><literal>NONE</literal> − нет заголовков</para> - </listitem> - <listitem> - <para><literal>ICON</literal> − пиктограмма</para> - </listitem> - </itemizedlist> - <para>Следующим обязательным элементом для таблицы является элемент <sgmltag id="element_table_columns">columns</sgmltag>. Он определяет набор колонок (<sgmltag>column</sgmltag>) таблицы.</para> - <para><sgmltag id="element_table_columns_column_1">column</sgmltag> − элемент, задающий опции для колонки таблицы. Может содержать следующие атрибуты:</para> - <itemizedlist> - <listitem> - <para><sgmltag id="attr_table_column_id">id</sgmltag> − обязательный атрибут, содержит название атрибута <glossterm linkend="entity">сущности</glossterm>, выводимого в колонке.</para> - </listitem> - <listitem> - <para><sgmltag id="attr_table_column_collapsed">collapsed</sgmltag> − необязательный атрибут, скрывает/показывает колонку. По умолчанию имеет значение <literal>false</literal>.</para> - </listitem> - <listitem> - <para><sgmltag id="attr_table_column_caption">caption</sgmltag> − необязательный атрибут, содержит заголовок колонки.</para> - </listitem> - <listitem> - <para><sgmltag id="attr_table_column_width">width</sgmltag> − необязательный атрибут, отвечает за изначальную ширину колонки.</para> - </listitem> - <listitem> - <para><sgmltag id="attr_table_column_calculatable">calculatable</sgmltag> − необязательный атрибут, используется только в редактируемой таблице. Буквально означает, что значения в данной колонке являются вычислимыми и зависят от значений других колонок.</para> - </listitem> - <listitem> - <para><link linkend="attr_editable" id="attr_table_column_editable">editable</link> − необязательный атрибут, разрешает/запрещает редактирование данного атрибута в редактируемой таблице.</para> - </listitem> - </itemizedlist> - <para>Для таблицы можно задать стиль отображения ячеек таблицы. Для этого в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана нужно задать для таблицы <code>StyleProvider</code> реализовав интерфейс <code>com.haulmont.cuba.gui.components.StyleProvider.</code></para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/tableStyle.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Далее нужно определить в теме приложения стили для строк и столбцов (подробную информацию о том, как создать тему приложения, смотрите <xref linkend="gui_themes"/>):</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/tableStylecss.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="381" align="center" fileref="img/gui_table_Style.png"/> - </imageobject> - </mediaobject> - </figure> - <para>В таблице существует возможность задавать собственное представление данных в колонке. Для этого необходимо использовать метод <code>Table.addGeneratedColumn</code>. В методе нужно создать CUBA-компонент с использованием класса <code>ComponentsFactory</code>. Пример:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/tableGeneratedColumn.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="501" align="center" fileref="img/gui_tableGenerated.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>table</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_aggregatable">aggregatable</link> - </entry>editable<entry align="left">margin</entry><entry align="left"> - <link linkend="attr_sortable">sortable</link> - </entry><entry/></row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry align="left"> - <link linkend="attr_table_multiselect">multiselect</link> - </entry> - <entry> - <link linkend="attr_stylename">styleName</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry align="left"> - <link linkend="attr_presentations">presentations</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry align="left"> - <link linkend="attr_showTotalAggregation">showTotalAggregation</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>table</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"> - <colspec colname="c1"/> - <tbody> - <row> - <entry> - <link linkend="element_table_actions">actions</link> - </entry> - </row> - <row>buttonsPanel<entry> - <link linkend="gui_ButtonsPanel">buttonsPanel</link> - </entry></row> - <row> - <entry> - <link linkend="attr_rowsCount">rowsCount</link> - </entry> - </row> - <row> - <entry> - <link linkend="element_t_columns">columns</link> - </entry> - </row> - <row> - <entry> - <link linkend="element_t_rows">rows</link> - </entry> - </row> - </tbody> - </tgroup> - </informaltable> - <para>Атрибуты элемента <link linkend="element_table_columns" id="element_t_columns">columns</link> <link linkend="element_table_columns_column_1">column</link>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="gui_Table_aggregation">aggregation</link> - </entry>editable<entry align="left"> - <link linkend="attr_clickAction">clickAction</link> - </entry><entry align="left"> - <link linkend="attr_table_column_id">id</link> - </entry><entry> - <link linkend="attr_resolution">resolution</link> - </entry></row> - <row> - <entry> - <link linkend="attr_table_column_calculatable">calculatable</link> - </entry> - <entry align="left"> - <link linkend="attr_table_column_collapsed">collapsed</link> - </entry> - <entry> - <link linkend="attr_optionsDatasource">optionsDatasource</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_table_column_caption">caption</link> - </entry> - <entry align="left"> - <link linkend="attr_dateFormat">dateFormat</link> - </entry> - <entry> - <link linkend="attr_required">required</link> - </entry> - <entry> - <link linkend="attr_table_column_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_captionProperty">captionProperty</link> - </entry> - <entry align="left"> - <link linkend="attr_table_column_editable">editable</link> - </entry> - <entry> - <link linkend="attr_requiredMessage">requiredMessage</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты <link linkend="element_table_rows" id="element_t_rows">rows</link>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_datasource">datasource</link> - </entry>editable</row> - <row> - <entry> - <link linkend="attr_headerMode">headerMode</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_TreeTable"> - <title>TreeTable - Таблица с иерархией (TreeTable) − таблица, отображающая иерархию сущностей. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="321" align="center" fileref="img/gui_treeTable.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>treeTable</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="90%" align="center" fileref="img/gui_TreeTable_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Для того чтобы создать иерархическую таблицу, необходимо в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> задать иерархический <link linkend="datasources">источник данных</link> и определить описание таблицы:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/treeTable/treeTable.txt" encoding="UTF-8" parse="text"/></programlisting> - <warning> - <para>Обязательными для иерархической таблицы являются элементы <link linkend="element_table_columns">columns</link> и <link linkend="element_table_rows">rows</link>.</para> - </warning> - </section> - <section id="gui_GroupTable"> - <title>GroupTable - Таблица с возможностью динамической группировки по любому полю. Для того чтобы сгруппировать таблицу по какому-либо столбцу, нужно в шапке таблицы перетащить этот столбец перед знаком - - - - . Сгруппированные значения можно разворачивать и сворачивать с помощью знаков - - - - / - - - - . -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="357" align="center" fileref="img/gui_groupTableDragColumn.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>groupTable</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_GroupTable_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Для правильного функционирования <code>GroupTable</code> должна быть соединена с <link linkend="datasources">GroupDatasource</link>.</para> - <para>Пример использования:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/groupTable/groupTable.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Элемент <sgmltag>group</sgmltag> − необязательный элемент, может в единственном экземпляре находиться внутри <link linkend="element_table_columns">columns</link>. Содержит набор элементов <link linkend="element_table_columns_column_1">column</link>, по которым будет выполняться первоначальная группировка.</para> - </section> - <section id="gui_Tree"> - <title>Tree - Компонент Tree представляет иерархическую структуру данных, отображая ее в виде дерева. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="100" align="center" fileref="img/Tree.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>tree</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_tree_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Для того чтобы создать компонент дерева, создайте в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующий код:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tree/tree.txt" encoding="UTF-8" parse="text"/></programlisting> - <warning> - <para>Обязательными для дерева являются элемент <sgmltag>treechildren</sgmltag>.</para> - </warning> - <para>Элемент <sgmltag id="element_treechildren">treechildren</sgmltag> декларирует используемый <link linkend="datasources">источник данных</link>. </para> - <para>Атрибуты <sgmltag>tree</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_height">height</link> - </entry>editable</row> - <row><entry> - <link linkend="attr_id">id</link> - </entry>required<entry/></row> - <row> - <entry align="left"> - <link linkend="attr_table_multiselect">multiselect</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_width">width</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>tree</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"><colspec colname="c1"/>c <tbody> - <row><entry align="left"> - <link linkend="element_treechildren">treechildren</link> - </entry>editable</row> - <row><entry> - <link linkend="element_table_actions">actions</link> - </entry>required</row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты <sgmltag>treechildren</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_captionProperty">captionProperty</link> - </entry>editable</row> - <row><entry> - <link linkend="attr_datasource">datasource</link> - </entry>required</row> - <row> - <entry align="left"> - <sgmltag>hierarchyProperty</sgmltag> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_ProgressBar"> - <title>ProgressBar - Индикатор прогресса (ProgressBar) наглядно демонстрирует пользователю, на каком этапе выполнения находится некий процесс, так что пользователь сможет понять, как долго остается ждать его завершения. Индикатор выводится на -экран в виде непрерывно меняющейся полосы. Может отображать конкретное значение прогресса, например, 30%, а может отображать неопределенное состояние. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_progressBar.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>progressBar</sgmltag></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_progressBar_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Для создания индикатора необходимо в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> определить его описание:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/progressBar/progressBar.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>С помощью атрибута <sgmltag id="attr_indeterminate">indeterminate</sgmltag> можно задать отображение неопределенного состояния индикатора. Если значение атрибута равно <literal>true</literal>, то индикатор отображает неопределенное состояние. Значение атрибута по умолчанию равно <literal>true</literal>.</para> - <para>Значения прогресса задачи для индикатора прогресса следует передавать в значениях типа <code>Float</code> в диапазоне от 0.0 до 1.0.</para> - <para>Атрибуты <sgmltag>progressBar</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_align">align</link> - </entry>editable<entry align="left"> - <link linkend="attr_height">height</link> - </entry><entry align="left"> - <link linkend="attr_stylename">stylename</link> - </entry><entry/></row> - <row> - <entry> - <link linkend="attr_editable">editable</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_enable">enable</link> - </entry> - <entry align="left"> - <link linkend="attr_indeterminate">indeterminate</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_FileUploadField"> - <title>FileUploadField - Компонент загрузки файлов (FileUploadField) позволяет пользователю загружать файлы на сервер. Компонент представляет собой кнопку, при нажатии на которую на экране отображается окно загрузки файла, содержащее список файлов некоторой директории. В списке можно выбрать только один файл для загрузки. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_upload.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>upload</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_FileUploadField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Ниже расположен пример, содержащий обработчик загрузки файла. В этом обработчике написан программный код, обеспечивающий вывод на экран сообщения об успешной или не успешной загрузке файла, а также вывод названия файла в компоненте <link linkend="gui_Label">надписи</link>.</para> - <para>Для начала определим описание компонента в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/upload/upload.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Программный код <glossterm linkend="screen_controller_glossentry">контроллера</glossterm> выглядит следующим образом:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/upload/uploadContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_uploadRez.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>upload</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_id">id</link> - </entry><entry align="left"> - <link linkend="attr_width">width</link> - </entry><entry/></row> - <row> - <entry> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry align="left"> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_FileMultiUploadField"> - <title>FileMultiUploadField - Компонент для множественной загрузки файлов позволяет пользователю загружать файлы на сервер. Компонент представляет собой кнопку, при нажатии на которую на экране отображается окно загрузки файлов, содержащее список файлов некоторой директории. В списке можно выбрать сразу несколько файлов для загрузки. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_multipleUpload.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>multiupload</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_FileMultiUploadField_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> - <para>Ниже расположен пример, содержащий обработчик загрузки файлов. В этом обработчике написан программный код, обеспечивающий вывод на экран сообщения об успешной или не успешной загрузке файлов, а также вывод названий файлов в <link linkend="gui_TextField">текстовое поле</link>.</para> - <para>Для начала определим описание компонента в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/multipleUpload/multipleUpload.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Программный код <glossterm linkend="screen_controller_glossentry">контроллера</glossterm> выглядит следующим образом:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/multipleUpload/multipleUploadContr.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_multipleUploadRez.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>multiupload</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_id">id</link> - </entry><entry align="left"> - <link linkend="attr_width">width</link> - </entry><entry/></row> - <row> - <entry> - <link linkend="attr_description">description</link> - </entry> - <entry align="left"> - <link linkend="attr_stylename">stylename</link> - </entry> - <entry/> - <entry/> - </row> - <row> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry align="left"> - <link linkend="attr_visible">visible</link> - </entry> - <entry/> - <entry/> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_Filter"> - <title>Filter - Универсальный фильтр (Filter) − компонент для организации поиска данных, предназначен для отбора информации, отображаемой в таблице экземпляров сущностей. - Типичный фильтр имеет следующий вид: -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_descr.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Для того чтобы создать фильтр, нажмите на кнопку <guibutton>Фильтр</guibutton> и выберите значение <guilabel>Создать</guilabel>. На экране отобразится панель редактора фильтра.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_editor.png"/> - </imageobject> - </mediaobject> - </figure> - <para>В поле <guilabel>Имя</guilabel> следует ввести имя фильтра, это имя будет отображаться в списке доступных для текущего экрана фильтров.</para> - <para>В таблице содержатся условия фильтра. Условия можно менять местами с помощью кнопок <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_filter_cond_down.png"/> - </imageobject> - </inlinemediaobject>/<inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_filter_cond_up.png"/> - </imageobject> - </inlinemediaobject>. Созданные условия можно удалять с помощью кнопки <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_filter_remove.png"/> - </imageobject> - </inlinemediaobject></para> - <para>С помощью флажков можно сделать выбранное в таблице условие скрытым для пользователя (колонка <guilabel>Скрытый</guilabel>) или обязательным для заполнения (колонка <guilabel>Обязательный</guilabel>). В колонке <guilabel>Операция</guilabel> следует для <emphasis role="bold">каждого</emphasis> условия выбрать операцию из списка доступных операций.</para> - <para>Фильтр можно сделать <firstterm>глобальным</firstterm> (то есть доступным для всех пользователей) с помощью установки флажка <guilabel>Общий для всех пользователей</guilabel>, или установить текущий фильтр в качестве фильтра по умолчанию с помощью установки флажка <guilabel>По умолчанию</guilabel>.</para> - <para>Чтобы добавить новое условие, следует нажать кнопку <guibutton>Добавить условие</guibutton> в редакторе фильтра. На экране отобразится окно выбора условий. </para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_conditions.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Условия разделяются по группам условий. Группа <guilabel>Атрибуты</guilabel> − это те условия, которые перечислены в элементах <sgmltag>properties</sgmltag> и <sgmltag>property</sgmltag> компонента фильтра. Группа <guilabel>Специальные условия</guilabel> − это пользовательские условия, заданные в элементе <sgmltag>custom</sgmltag>. Группы <guilabel>Группы</guilabel>, <guilabel>Динамический атрибут...</guilabel> и <guilabel>Создать новое...</guilabel> доступны для выбора всегда.</para> - <para/> - <para>XML-имя компонента: <sgmltag>filter</sgmltag>.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_filter_dia.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Существует два класса компонентов фильтра: для веб-приложений и для десктоп-приложений.</para> - <para>Компонент фильтра имеет следующие атрибуты, перечисленные ниже.</para> - <para><sgmltag id="applyTo_attr">applyTo</sgmltag> − необязательный атрибут, содержит <link linkend="attr_id">идентификатор</link> компонента, с которым связан фильтр. Используется в случае, когда необходимо иметь доступ к <link linkend="gui_Table_presentations">представлениям</link> связного компонента. Например, сохраняя фильтр как <link linkend="search_folder">папку поиска</link> или как <link linkend="application_folder">папку приложения</link>, можно указать, какое представление будет применятся при просмотре этой папки.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_apply_to.png"/> - </imageobject> - </mediaobject> - </figure> - <para><sgmltag id="useMaxResults_attr">useMaxResults</sgmltag> − необязательный атрибут, при указании значения <literal>false</literal> фильтр не будет отображать флажок <guilabel>Показывать N строк</guilabel>. По умолчанию флажок отображается, если есть специфическое разрешение <property>cuba.gui.filter.maxResults</property> (подробнее о специфических разрешениях смотрите в <productname>Руководстве по безопасности платформы <trademark>CUBA</trademark></productname> (<ulink url="http://docs.haulmont.com/cuba/">http://docs.haulmont.com/cuba/</ulink>)). Если атрибут <sgmltag>useMaxResults</sgmltag> не указан или значение атрибута равно <literal>true</literal>, а разрешение <property>cuba.gui.filter.maxResults</property> запрещено, фильтр будет принудительно отбирать только первые N строк без возможности пользователя отключить это. Число N определяется параметрами <literal>FetchUI</literal>, <literal>DefaultFetchUI</literal>, задаваемыми в <code>PersistenceManager</code>.</para> - <para>На рисунке далее показан вид фильтра со значением атрибута <code>useMaxResults="true"</code>, запретом специфического разрешения <property>cuba.gui.filter.maxResults</property> и параметром <code>DefaultFetchUI=2</code></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_useMaxRezult.png"/> - </imageobject> - </mediaobject> - </figure> - <para><sgmltag id="manualApplyRequired_attr">manualApplyRequired</sgmltag> − необязательный атрибут. Определяет, как будет применяться фильтр. Если значение атрибута равно <literal>false</literal>, то фильтр будет применяться сразу при открытии экрана списка. Если значение атрибута равно true, то фильтр будет применяться только после нажатия на кнопку <guibutton>Применить</guibutton>. Данный атрибут имеет приоритет над общесистемным <link linkend="app_properties_glossentry">свойством</link> <property>cuba.gui.genericFilterManualApplyRequired</property>.</para> - <para>Если значение атрибута <sgmltag id="required_filter_attr">required</sgmltag> равно <literal>true</literal>, то в списке фильтров значение <literal><без фильтрации></literal> не отображается, и должен быть выбран один из доступных фильтров. Если для экрана не установлен фильтр по умолчанию, то в списке выбора фильтра автоматически устанавливается первый созданный фильтр.</para> - <para>Если значение атрибута <sgmltag id="editable_filter_attr">editable</sgmltag> равно <literal>false</literal>, то кнопка <guibutton>Фильтр</guibutton> скрывается.</para> - <para>Компонент <sgmltag>filter</sgmltag> может содержать следующие элементы:</para> - <variablelist> - <varlistentry> - <term> - <sgmltag>properties</sgmltag> - </term> - <listitem> - <para>Элемент, определяющий набор атрибутов <glossterm linkend="entity">сущности</glossterm>, заданной <link linkend="datasources">источником данных</link>. В качестве названий атрибутов используются <glossterm linkend="localization">локализованные имена атрибутов сущностей</glossterm>. Атрибуты:</para> - <itemizedlist> - <listitem> - <para><sgmltag id="include_filter_attr">include</sgmltag> − обязательный атрибут, содержит регулярное выражение для включения атрибутов сущности. Атрибуты-коллекции не включаются независимо от данного регулярного выражения.</para> - <para>Чтобы включить все атрибуты сущности, используйте значение атрибута <literal>.*</literal> </para> - </listitem> - <listitem> - <para><sgmltag id="exclude_filter_attr">exclude</sgmltag> − необязательный атрибут, содержит регулярное выражение для исключения атрибутов сущности из предварительно включенных с помощью <link linkend="include_filter_attr">include</link>.</para> - <para>Чтобы исключить несколько атрибутов, перечислите их в значении атрибута <link linkend="exclude_filter_attr">exclude</link>, разделяя символом <literal>|</literal></para> - </listitem> - </itemizedlist> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_properties.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_properties.png"/> - </imageobject> - </mediaobject> - </figure> - </listitem> - </varlistentry> - <varlistentry> - <term> - <sgmltag>property</sgmltag> - </term> - <listitem> - <para>Элемент, определяющий один атрибут сущности. Атрибуты:</para> - <itemizedlist> - <listitem> - <para><sgmltag id="name_filter_property_attr">name</sgmltag> − имя атрибута сущности. Может быть путем (через ".") по графу <glossterm linkend="entity">сущностей</glossterm>.</para> - <warning> - <para>Если в качестве имени атрибута указан путь по графу сущностей, обязательно указание значения для атрибута <link linkend="caption_filter_attr">caption</link>.</para> - </warning> - </listitem> - <listitem> - <para><sgmltag id="caption_filter_attr">caption</sgmltag> − атрибут для задания локализованного названия имени параметра</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_property_caption.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_property_caption.png"/> - </imageobject> - </mediaobject> - </figure> - </listitem> - <listitem> - <para><sgmltag id="paramWhere_filter_attr">paramWhere</sgmltag> − необязательный атрибут для задания фильтра на список значений параметра-<glossterm linkend="entity">сущности</glossterm>. </para> - <para>Например, можно ограничить список предлагаемых пользователю моделей автомобилей только моделями марки <literal>Audi</literal>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_paramWhere.txt" encoding="UTF-8" parse="text"/></programlisting> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_paramWhere.png"/> - </imageobject> - </mediaobject> - </figure> - </listitem> - <listitem> - <para><sgmltag id="paramView_filter_attr">paramView</sgmltag> − необязательный атрибут для задания <link linkend="views">представления</link>, с которым будут загружаться список значений параметра-<glossterm linkend="entity">сущности</glossterm>. Например, <literal>_local</literal>.</para> - </listitem> - </itemizedlist> - </listitem> - </varlistentry> - <varlistentry> - <term> - <sgmltag>custom</sgmltag> - </term> - <listitem> - <para>Элемент, определяющий произвольное условие. Содержимым элемента должно быть выражение на <glossterm linkend="jpql">JPQL</glossterm> (возможно использование JPQL Macros), которое будет добавлено в условие <code>where</code> результирующего запроса. Параметр (если он есть) обозначается символом <literal>?</literal>. </para> - <para>Пример фильтра с произвольными условиями:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_custom.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Созданные произвольные условия попадают в раздел <guilabel>Специальные условия</guilabel> в редакторе условий.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_custom.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты элемента <sgmltag>custom</sgmltag>:</para> - <itemizedlist> - <listitem> - <para><sgmltag id="name_filter_custom_attr">name</sgmltag> − имя условия</para> - </listitem> - <listitem> - <para><sgmltag id="caption_filter_custom_attr">caption</sgmltag> − <link linkend="localization">локализованное</link> название условия</para> - </listitem> - <listitem> - <para><sgmltag id="paramClass_filter_attr">paramClass</sgmltag> или <sgmltag>class</sgmltag> − класс параметра условия</para> - </listitem> - <listitem> - <para><sgmltag id="inExpr_filter_attr">inExpr</sgmltag> − значение должно быть равно <literal>true</literal>, если выражение <glossterm linkend="jpql">JPQL</glossterm> содержит условие <code>in (?)</code></para> - </listitem> - <listitem> - <para><sgmltag id="join_filter_attr">join</sgmltag> − необязательный атрибут для задания строки, которая будет добавлена в секцию <literal>join</literal> запроса.</para> - </listitem> - <listitem> - <para><sgmltag>paramWhere</sgmltag> − необязательный атрибут для задания фильтра на список значений параметра-сущности.</para> - </listitem> - <listitem> - <para><sgmltag>paramView</sgmltag> − необязательный атрибут для задания <link linkend="views">представления</link>, с которым будут загружаться список значений параметра-<glossterm linkend="entity">сущности</glossterm>. Например, <literal>_local</literal>.</para> - </listitem> - </itemizedlist> - </listitem> - </varlistentry> - </variablelist> - <para>Атрибут <sgmltag>paramWhere</sgmltag> − необязательный атрибут для задания фильтра на список значений параметра-сущности. Содержит условие в формате <glossterm linkend="jpql">JPQL</glossterm> без слова <literal>where</literal>. Алиас сущности можно задавать любым. - -Можно использовать параметры экрана, атрибуты сессии, а также <link linkend="component_filter_name_caution">компоненты</link> экрана, в том числе отображающие другие параметры.</para> - <caution id="component_filter_name_caution"> - <title>Подсказка - Как узнать имя компонента, отображающего параметр? - Для этого нужно в редакторе фильтра правой кнопкой мыши вызвать контекстное меню в строке таблицы условий и выбрать в нем значение Показать имя компонента. После этого на экране отобразится окно, содержащее имя компонента. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_component_name.png"/> - </imageobject> - </mediaobject> - </figure> - </caution> - <para><emphasis role="bold">Права пользователей</emphasis></para> - <itemizedlist> - <listitem> - <para>Для создания/изменения/удаления глобальных фильтров пользователь должен иметь разрешение <property>cuba.gui.filter.global</property> </para> - </listitem> - <listitem> - <para>Для создания/изменения новых условий пользователь должен иметь разрешение <property>cuba.gui.filter.customConditions</property></para> - </listitem> - <listitem> - <para>Для возможности изменять значение флажка <guilabel>Show first N rows</guilabel> пользователь должен иметь разрешение <property>cuba.gui.filter.maxResults</property></para> - </listitem> - </itemizedlist> - <para>Информацию о том, как настраивать специфические разрешения, смотрите в <productname>Руководстве по безопасности платформы <trademark>CUBA</trademark></productname> (<ulink url="http://docs.haulmont.com/cuba/">http://docs.haulmont.com/cuba/</ulink>)</para> - <para><emphasis role="bold">Общесистемные параметры</emphasis></para> - <para>В системе предусмотрены следующие <glossterm linkend="app_properties_glossentry">параметры</glossterm>, влияющие на поведение фильтров:</para> - <itemizedlist> - <listitem> - <para><property>cuba.gui.genericFilterManualApplyRequired</property> − означает, что фильтр будет применяться только после нажатия пользователем соответствующей кнопки <guibutton>Применить</guibutton>. Значение по умолчанию равно <literal>true</literal>. Если выставлено значение <literal>false</literal>, то фильтр будет применяться сразу при открытии экрана списка. При открытии экрана списка с помощью <link linkend="application_folder">папки приложения</link> или <link linkend="search_folder">папки поиска</link> значение <property>cuba.gui.genericFilterManualApplyRequired</property> не учитывается. По умолчанию в этом случае в экране поиска фильтр будет применяться. Фильтр не применится, если значение атрибута <code>applyDefault</code> у папки явно установлено в <literal>false</literal>.</para> - </listitem> - <listitem> - <para><property>cuba.gui.genericFilterChecking</property> − означает, что перед применением фильтра будет выполняться проверка его условий. Если фильтр не имеет ни одного заданного условия, то применяться он не будет. Значение по умолчанию равно <literal>true</literal>. Если значение равно <literal>false</literal>, то проверка не производится.</para> - </listitem> - <listitem> - <para><property>cuba.gui.genericFilterTreeConditionSelect</property> − включает выбор условий в древовидной структуре с возможностью обхода свойств связанных сущностей. Значение по умолчанию равно <literal>true</literal>. В случае задания значения <literal>false</literal> выбор условий осуществляется в плоском выпадающем списке из описателей условий, заданных в дескрипторе экрана.</para> - </listitem> - <listitem> - <para><property>cuba.allowQueryFromSelected</property> служит для включения механизма <link linkend="sequential_filter_para">последовательного наложения фильтров</link>. Значение по умолчанию равно <literal>true</literal>.</para> - </listitem> - </itemizedlist> - <para><emphasis role="bold">Параметры вызова экрана</emphasis></para> - <para>Существует возможность указать при вызове экрана, какой фильтр и с какими параметрами применить. Фильтр должен быть заранее создан в базе данных и иметь заполненное поле <database>code</database>.</para> - <para>Для указания кода фильтра в экран следует передать параметр с именем, равным <link linkend="attr_id">идентификатору</link> компонента фильтра в экране. Значение параметра − код фильтра, который нужно применить.</para> - <para>Для установки значений параметров фильтра нужно передать параметры с именами, равными <link linkend="component_filter_name_caution">именам параметров</link>, и значения в виде строк.</para> - <para>Пример установки фильтра из главного меню:</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_from_menu.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Следует отметить, что фильтр с установленным полем <database>code</database> обладает особыми свойствами:</para> - <itemizedlist> - <listitem> - <para>Его не могут редактировать пользователи</para> - </listitem> - <listitem> - <para>Название такого фильтра локализуется − в <link linkend="main_message_pack">главном пакете сообщений</link> приложения должна быть строка с ключом, равным коду фильтра.</para> - </listitem> - </itemizedlist> - <para id="sequential_filter_para"><emphasis role="bold">Последовательное наложение фильтров</emphasis></para> - <para>При включенном свойстве приложения <property> - <link linkend="cuba.allowQueryFromSelected">cuba.allowQueryFromSelected</link> - </property> в пользовательском интерфейсе компонента можно закреплять последний примененный фильтр и текущие результаты фильтрации. После этого можно выбрать другой фильтр или параметры и применить их на уже выбранных записях.</para> - <para>Данный подход позволяет решить две проблемы:</para> - <itemizedlist> - <listitem> - <para>Декомпозировать сложные фильтры.</para> - </listitem> - <listitem> - <para>Применять фильтры на записи, отобранные с помощью папок <link linkend="application_folder">приложения</link> или <link linkend="search_folder">поиска</link>.</para> - </listitem> - </itemizedlist> - <para>Чтобы применить этот механизм в пользовательском интерфейсе, выберите и примените один из фильтров. Затем нажмите на кнопку <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_filter_add.png"/> - </imageobject> - </inlinemediaobject>. Фильтр закрепится в нижней части панели фильтра. Далее можно применить к выбранным записям другой фильтр. Так последовательно можно накладывать друг на друга любое количество фильтров. Также фильтры можно удалять последовательно с помощью кнопки <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_filter_remove.png"/> - </imageobject> - </inlinemediaobject></para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_filter_sequential.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Механизм последовательного наложения фильтров основан на возможности <code> - <link linkend="dataService">DataWorker</link> - </code> выполнять <link linkend="query_from_selected">последовательные запросы</link>.</para> - <warning> - <para>На момент написания данного руководства механизм последовательного наложения фильтров реализован только для блока <structname>Web Client</structname>.</para> - </warning> - <para>Атрибуты <sgmltag>filter</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="applyTo_attr">applyTo</link> - </entry>editable<entry align="left"> - <link linkend="attr_id">id</link> - </entry><entry align="left"> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row> - <entry> - <link linkend="attr_datasource">datasource</link> - </entry> - <entry align="left"> - <link linkend="manualApplyRequired_attr">manualApplyRequired</link> - </entry> - <entry> - <link linkend="useMaxResults_attr">useMaxResults</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="editable_filter_attr">editable</link> - </entry> - <entry align="left"> - <link linkend="required_filter_attr">required</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты элемента <sgmltag>properties</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="1" colsep="1" rowsep="1" align="left"><colspec colname="c1"/>c <tbody> - <row><entry align="left"> - <link linkend="include_filter_attr">include</link> - </entry>editable</row> - <row> - <entry> - <link linkend="exclude_filter_attr">exclude</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты элемента <sgmltag>property</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="caption_filter_attr">caption</link> - </entry>editable<entry align="left"> - <link linkend="paramView_filter_attr">paramView</link> - </entry></row> - <row> - <entry> - <link linkend="name_filter_property_attr">name</link> - </entry> - <entry align="left"> - <link linkend="paramWhere_filter_attr">paramWhere</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты элемента <sgmltag>custom</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="caption_filter_custom_attr">caption</link> - </entry>editable<entry align="left">operatorType</entry><entry align="left"> - <link linkend="paramWhere_filter_attr">paramWhere</link> - </entry></row> - <row> - <entry> - <link linkend="join_filter_attr">join</link> - </entry> - <entry align="left"> - <link linkend="paramClass_filter_attr">paramClass</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="name_filter_custom_attr">name</link> - </entry> - <entry align="left"> - <link linkend="paramView_filter_attr">paramView</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - </section> - <section id="gui_layouts"> - <title>Контейнеры - BoxLayout - ButtonsPanel - GridLayout - ScrollBoxLayout - SplitPanel - GroupBoxLayout - TabSheet - IFrame -
- BoxLayout - BoxLayout представляет собой контейнер с последовательным размещением компонентов. - Существует 3 типа BoxLayout, определяемых именем XML-элемента: - - - hbox − горизонтальное расположение компонентов -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_hbox.png"/> - </imageobject> - </mediaobject> - </figure> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/box/hbox.txt" encoding="UTF-8" parse="text"/></programlisting> - </listitem> - <listitem> - <para><sgmltag>vbox</sgmltag> − вертикальное расположение компонентов. <sgmltag>vbox</sgmltag> имеет 100% ширину по умолчанию</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="182" contentdepth="245" align="center" fileref="img/gui_vbox.png"/> - </imageobject> - </mediaobject> - </figure> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/box/vbox.txt" encoding="UTF-8" parse="text"/></programlisting> - </listitem> - <listitem> - <para><sgmltag>flowbox</sgmltag> − горизонтальное расположение компонентов с переносом вниз. При недостатке места по горизонтали непомещающиеся компоненты будут перенесены "на следующую строку" (поведение аналогично <application>Swing</application> <code>FlowLayout</code>)</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_flowbox.png"/> - </imageobject> - </mediaobject> - </figure> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/box/flowbox.txt" encoding="UTF-8" parse="text"/></programlisting> - </listitem> - </itemizedlist> - <para>Могут быть использованы следующие XML-атрибуты:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_align">align</link> - </entry>editable<entry align="left"> - <link linkend="attr_id">id</link> - </entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row> - <entry> - <link linkend="attr_expand">expand</link> - </entry> - <entry align="left"> - <link linkend="attr_margin">margin</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_spacing">spacing</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_ButtonsPanel"> - <title>ButtonsPanel - Компонент, унифицирующий использование и размещение кнопок для управления данными в таблице. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="272" align="center" fileref="img/gui_buttonsPanel.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>buttonsPanel</sgmltag>.</para> - <para>Для создания панели с кнопками нужно определить ее описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/buttonsPanel/buttonsPanel.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибут <sgmltag id="attr_alwaysVisible">alwaysVisible</sgmltag> служит для управления видимостью панели с кнопками при открытии окна списка в режиме поиска (например, при открытии окна списка из компонента <link linkend="gui_PickerField">PickerField</link>). Если значение атрибута равно <literal>true</literal>, то панель с кнопками не скрывается. По умолчанию значение атрибута равно <literal>false</literal>.</para> - <para>Элементами панели с кнопками являются элементы <link linkend="gui_Button">button</link>.</para> - <para>Атрибуты <sgmltag>buttonsPanel</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row> - <entry align="left"> - <link linkend="attr_align">align</link> - </entry> - <entry align="left"> - <link linkend="attr_id">id</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_alwaysVisible">alwaysVisible</link> - </entry> - <entry align="left"> - <link linkend="attr_stylename">styleName</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_expand">expand</link> - </entry> - <entry align="left"> - <link linkend="attr_visible">visible</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_height">height</link> - </entry> - <entry align="left"> - <link linkend="attr_width">width</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_GridLayout"> - <title>GridLayout - Контейнер, располагающий компоненты по сетке. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_gridlayout.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>grid</sgmltag>.</para> - <para>Для создания контейнера с табличным расположением нужно определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/grid/grid.txt" encoding="UTF-8" parse="text"/></programlisting> - <warning> - <para>Обязательными вложенными элементами являются элементы <sgmltag>columns</sgmltag> и <sgmltag>rows</sgmltag>. </para> - </warning> - <para><sgmltag id="attr_layout_rows">rows</sgmltag> − обязательный элемент, содержит последовательность строк (<sgmltag>row</sgmltag>). Хотя бы один элемент строки является обязательным.</para> - <para><sgmltag id="attr_layout_row">row</sgmltag> − элемент строки, контейнер для содержимого ряда. Состоит из контейнеров или компонентов.</para> - <para><sgmltag id="attr_grid_flex_row">flex</sgmltag> − необязательный атрибут для элемента <sgmltag>row</sgmltag>, задает соотношение высот рядов сетки.</para> - <para><sgmltag id="attr_grid_columns">columns</sgmltag> − обязательный элемент, содержит последовательность <sgmltag>column</sgmltag>.</para> - <para>Атрибут <sgmltag id="attr_grid_count">count</sgmltag> − необязательный атрибут, задает количество колонок, если не заданы элементы <sgmltag>column</sgmltag>.</para> - <para><sgmltag id="attr_grid_column">column</sgmltag> − необязательный элемент для элемента <link linkend="attr_grid_columns">columns</link>, декларирует атрибуты колонок сетки.</para> - <para>Атрибут <sgmltag id="attr_grid_flex_column">flex</sgmltag> − необязательный атрибут, задает соотношение ширин колонок.</para> - <para>Атрибуты <sgmltag>grid</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_height">height</link> - </entry>editable<entry align="left"> - <link linkend="attr_spacing">spacing</link> - </entry><entry> - <link linkend="attr_width">width</link> - </entry></row> - <row> - <entry> - <link linkend="attr_id">id</link> - </entry> - <entry align="left"> - <link linkend="attr_stylename">styleName</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_margin">margin</link> - </entry> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Элементы <sgmltag>grid</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_grid_columns">columns</link> - </entry>editable</row> - <row> - <entry> - <link linkend="attr_layout_rows">rows</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты <sgmltag>columns</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_grid_count">count</link> - </entry>editable</row> - </tbody></tgroup> - </informaltable> - <para>Атрибуты <sgmltag>row</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_align">align</link> - </entry>editable</row> - <row> - <entry> - <link linkend="attr_grid_flex_row">flex</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_spacing">spacing</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_visible">visible</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_ScrollBoxLayout"> - <title>ScrollBoxLayout - ScrollBoxLayout − контейнер, который позволяет прокручивать свое содержимое. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_scrollBox.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>scrollBox</sgmltag></para> - <para>Для создания контейнера с табличным расположением нужно определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/scrollBox/scrollBox.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>С помощью атрибута <sgmltag id="attr_scroll_orientation">orientation</sgmltag> можно задавать направление расположения вложенных компонентов − <literal>horizontal</literal> или <literal>vertical</literal>. По умолчанию значением атрибута является <literal>vertical</literal>.</para> - <warning> - <para>Вложенные в <sgmltag>scrollBox</sgmltag> компоненты должны иметь фиксированные размеры или размеры по умолчанию. Нельзя устанавливать <code>height="100%"</code> или <code>width="100%"</code>.</para> - </warning> - <para>Атрибуты <sgmltag>scrollBox</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_align">align</link> - </entry><entry> - <link linkend="attr_scroll_orientation">orientation</link> - </entry>editable</row> - <row> - <entry> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_spacing">spacing</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_id">id</link> - </entry> - <entry> - <link linkend="attr_stylename">stylename</link> - </entry> - </row> - <row> - <entry> - <link linkend="attr_margin">margin</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_SplitPanel"> - <title>SplitPanel - Панель с разделителем (SplitPanel) − контейнер, разбитый на две области, размер которых по одному из направлений (горизонтали либо вертикали) можно менять путем перемещения разделителя. На рисунке показан пример разбивки области окна на три панели. Во внешней панели формируется горизонтальная граница, которая отделяет нижнюю панель от верхней. Верхняя панель разделяется по вертикали. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_splitPanel.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>split</sgmltag>.</para> - <para>Для создания панели с разделителем нужно определить ее описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/split/split.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>При создании панели с разделителем нужно указать ориентацию разделителя с помощью атрибута <sgmltag id="attr_layout_orientation">orientation</sgmltag>. По умолчанию значение атрибута равно <literal>vertical</literal>.</para> - <para id="attr_layout_pos"><sgmltag>pos</sgmltag> − необязательный атрибут, определяет процентное соотношение областей. Например, <code>pos="30%"</code> означает соотношение областей 30/70. По умолчанию соотношение областей составляет 50/50.</para> - <warning> - <para>Обязательными вложенными элементами являются два контейнера или компонента.</para> - </warning> - <para>Атрибуты <sgmltag>split</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_id">id</link> - </entry>editable<entry align="left"> - <link linkend="attr_layout_pos">pos</link> - </entry></row> - <row> - <entry> - <link linkend="attr_height">height</link> - </entry> - <entry align="left"> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_layout_orientation">orientation</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_GroupBoxLayout"> - <title>GroupBox - Контейнер, позволяющий выделить рамкой группу объектов, задать им общий заголовок и описание. Кроме того, он умеет сворачивать свое содержимое. -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="188" contentdepth="270" align="center" fileref="img/gui_groupBox.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>groupBox</sgmltag>.</para> - <para>Для создания контейнера с названием и рамкой нужно определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/groupBox/groupBox.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Атрибут <sgmltag id="attr_group_orientation">orientation</sgmltag> задает направление расположения вложенных компонентов − <literal>horizontal</literal> или <literal>vertical</literal>. По умолчанию значением атрибута является <literal>vertical</literal>.</para> - <para><sgmltag id="attr_group_collapsable">collapsable</sgmltag> − необязательный атрибут, в значении <literal>true</literal> компонент может сворачивать свое содержимое. В верхнем нижнем углу контейнера появляется значок <inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_groupBox_minus.png"/> - </imageobject> - </inlinemediaobject>/<inlinemediaobject> - <imageobject> - <imagedata fileref="img/gui_groupBox_plus.png"/> - </imageobject> - </inlinemediaobject>.</para> - <para><sgmltag id="attr_group_collapsed">collapsed</sgmltag> − необязательный атрибут, в значении <literal>true</literal> компонент будет свернут по умолчанию.</para> - <figure> - <title/> - <mediaobject> - <imageobject> - <imagedata contentwidth="80%" align="center" fileref="img/gui_groupBox_collapsed.png"/> - </imageobject> - </mediaobject> - </figure> - <para>Атрибуты <sgmltag>groupBox</sgmltag>:</para> - <informaltable frame="none" pgwide="0" align="left"> - <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> - <row><entry align="left"> - <link linkend="attr_caption">caption</link> - </entry>editable<entry align="left"> - <link linkend="attr_description">description</link> - </entry><entry> - <link linkend="attr_id">id</link> - </entry><entry> - <link linkend="attr_stylename">stylename</link> - </entry></row> - <row> - <entry> - <link linkend="attr_group_collapsable">collapsable</link> - </entry> - <entry align="left"> - <link linkend="attr_expand">expand</link> - </entry> - <entry> - <link linkend="attr_orientation">orientation</link> - </entry> - <entry> - <link linkend="attr_width">width</link> - </entry> - </row> - <row> - <entry align="left"> - <link linkend="attr_group_collapsed">collapsed</link> - </entry> - <entry> - <link linkend="attr_height">height</link> - </entry> - <entry> - <link linkend="attr_spacing">spacing</link> - </entry> - </row> - </tbody></tgroup> - </informaltable> - </section> - <section id="gui_TabSheet"> - <title>TabSheet - Панель с вкладками (TabSheet) позволяет выводить на экран вкладки (tabs) − панели, имеющие название. При нажатии пользователем на названии вкладки TabSheet выводит на экран соответствующие выбранной вкладке элементы пользовательского интерфейса. -
- - <mediaobject> - <imageobject> - <imagedata align="center" fileref="img/gui_tabsheet.png"/> - </imageobject> - </mediaobject> - </figure> - <para>XML-имя компонента: <sgmltag>tabSheet</sgmltag>.</para> - <para>Для создания панели с вкладками нужно определить ее описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> - <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tabsheet/tabsheet.txt" encoding="UTF-8" parse="text"/></programlisting> - <para>Как видно, компонент <sgmltag>tabSheet</sgmltag> состоит из элементов <sgmltag>tab</sgmltag>.</para> - <para>Элемент <sgmltag>tab</sgmltag> может содержать как другой <link linkend="gui_layouts">контейнер</link>, так и <link linkend="gui_components">компонент</link>.</para> - <para>Атрибут <link linkend="attr_id" id="attr_tabsheet_id">id</link> является идентификатором вкладки. Следует отметить, что вкладка не является компонентом, и данный идентификатор используется только в рамках <code>TabSheet</code>.</para> - <para>Атрибут <sgmltag id="attr_tabsheet_lazy">lazy</sgmltag> задает отложенную загрузку содержимого вкладки. При открытии экрана <sgmltag>lazy</sgmltag>-вкладки не загружают свое содержимое, что приводит к созданию меньшего количества компонентов в памяти и, как следствие, к меньшему количеству запросов к БД. Компоненты вкладки загружаются только в тот момент, когда пользователь выбирает данную вкладку. Значение по умолчанию равно <literal>false</literal>. Если компоненты <sgmltag>lazy</sgmltag>-вкладки требуют инициализации, проводить ее нужно не напрямую в методе <code>init()</code> <glossterm linkend="screen_controller_glossentry">контроллера</glossterm>, а в слушателе на переключение вкладок <code>TabSheet.TabChangeListener.</code></para> - <para>В десктоп-приложении есть возможность открывать вкладки в новом окне. Для этого нужно атрибуту <sgmltag id="attr_detachable">detachable</sgmltag> присвоить значение <literal>true</literal>.</para> - <figure> - <title>Вид отделяемой вкладки - - - - - -
- Атрибуты tabSheet: - - c - - height - editable - visible - - - - id - - - width - - - - - stylename - - - - - Атрибуты элемента tab: - - c - - caption - editable - expand - - margin - - - - detachable - - - id - - - spacing - - - - - enable - - - lazy - - - - -
-
- IFrame - Фреймы предназначены для декомпозиции и многократного использования частей экранов. - Фрейм включается в экран с помощью элемента iframe. - Для включения фрейма в экран необходимо в xml-дескрипторе определить его описание: - - Атрибут src − путь к XML-дескриптору фрейма. - Если фрейм зарегистрирован в файле screens.xml, то можно указать идентификатор фрейма в атрибуте screen. - - Обязательно должен быть указан один из атрибутов: src или screen. - - Дескриптор фрейма идентичен xml-дескриптору экрана и может содержать собственный metadataContext, dsContext, messagesPack. Кроме того, фрейм может иметь собственный контроллер. - Правила взаимодействия экрана и вложенного в него фрейма: - - - Из экрана обращаться к компонентам фрейма можно через точку: frame_id.component_id - - - Из контроллера фрейма получить компонент экрана можно обычным вызовом getComponent(component_id), но только в том случае, если компонент с таким именем не объявлен в самом фрейме. То есть компоненты фрейма маскируют компоненты экрана. - - - Из фрейма получить источник данных экрана можно простым вызовом getDsContext().get(ds_id) либо в запросе ds$ds_id, но только в том случае, если источник данных с таким именем не объявлен в самом фрейме (аналогично компонентам). - - - Из экрана получить источник данных фрейма можно только через итерацию по getDsContext().getChildren() - - - При коммите экрана вызывается также коммит измененных источников данных фрейма. - Атрибуты iframe: - - c - - align - editable - screen - - visible - - - - height - - - src - - - width - - - - - id - - - stylename - - - - -
-
-
- Разное -
- AccessControl - Компонент accessControl предназначен для декларативного управления доступом к частям экрана. Он представляет собой контейнер, который никак не отображает себя на экране, а только управляет атрибутами visible и editable входящих в него компонентов. - Состояние управляемых компонентов определяется на основе двух элементов, вложенных в accessControl: visible и editable. - Соответствующее значение принимается либо из скрипта, возвращающего boolean, либо из свойства объекта AccessData, связанного с компонентом (см. ниже). -Скрипт задается либо атрибутом script, либо in-place в тексте элемента. -Свойство объекта AccessData задается атрибутом property и имеет больший приоритет, чем скрипт. - - В случае visible=false вложенные компоненты вообще не создаются, поэтому будьте осторожны при обращении к ним из контроллера. - - - <tab id="mainTab" caption="msg://mainTab"> - <accessControl data="workflow.client.web.ui.card.CardAccessData" param="accessData"> - <editable property="notStarted"/> - - <vbox margin="true" expand="attachmentsPane"> - ... - -
- Объект AccessData - В связи с тем, что одни и те же параметры доступа могут многократно потребоваться в разных частях экрана и в коде контроллера, компонент accessControl может быть связан с объектом Java|Groovy, вычисляющим и хранящим эти параметры. Т.о. дорогостоящая инициализация параметра доступа производится только один раз, и затем доступна на протяжении жизни экрана. - Для связи компонента с данными доступа создайте класс, унаследованный от AbstractAccessData, и задайте следующие атрибуты компонента accessControl: - - - data − имя класса AccessData - - - param − имя параметра экрана, в котором будет сохранен объект AccessData - - - В момент создания экрана загрузчик компонента accessControl проверяет наличие экземпляра AccessData в указанном параметре, и если его там нет, создает новый экземпляр. - В дальнейшем в коде контроллера можно получить экземпляр AccessData из параметров экрана, например: - AbstractWfAccessData accessData = getContext().getParamValue("accessData"); - При создании класса AccessData следует иметь в виду, что в параметры экрана-редактора всегда передается параметр param$item, содержащий текущий редактируемый экземпляр сущности. Однако этот экземпляр загружен по view вызывающего экрана, а не по view редактора. - Свойства AccessData, на которые можно ссылаться из компонента accessControl, должны быть реализованы по стандарту JavaBeans: методами с сигнатурой boolean getXxx() - - - -
-
-
- Action - Action − интерфейс, абстрагирующий действие от визуального компонента. - Визуальный компонент, содержащий одно действие, реализует интерфейс Component.ActionOwner. Это, например, Button. - Визуальный компонент, содержащий несколько действий, реализует интерфейс Component.ActionsHolder. Это Window, IFrame, Table и ее наследники, Tree, PopupButton, PickerField, LookupPickerField. -
- Декларативное создание действий - В дескрипторе экрана может быть задан набор действий для некоторого ActionsHolder. - - Доступные атрибуты действия - - id − идентификатор, должен быть уникален в рамках данного ActionsHolder - - - caption - - - icon - - - enable - - - visible − в отличие от компонентов Groovy-выражения не поддерживаются - - - invoke − имя вызываемого метода контроллера. Метод должен быть public, не возвращать результата, и либо не иметь аргументов, либо иметь один аргумент типа Component. Если метод имеет аргумент Component, то при вызове в него будет передан экземпляр визуального компонента, запустившего данное действие. - - - shortcut − доступен только для действий, задаваемых в окне или фрейме. Задает комбинацию клавиш для вызова. - - - Примеры: - - - -
-
- Использование в кнопках - Действия, заданные для некоторого ActionsHolder, могут быть использованы в компонентах ActionOwner, например в Button. Для этого достаточно указать полное имя действия в соотв. атрибуте. Например: - - При этом Button возьмет значения атрибутов caption, icon, enable, visible из назначенного действия. -
-
- Стандартные действия - Для наследников ListComponent (это Table и Tree) существует набор стандартных действий, определяемых перечислением ListActionType. Для таких действий не нужно определять никаких атрибутов, кроме идентификатора. Например: - - Для компонентов PickerField и LookupPickerField существует набор стандартных действий, определяемых перечислением PickerField.ActionType. Для таких действий не нужно определять никаких атрибутов, кроме идентификатора. Например: - -
-
- Программное создание и управление действиями - Базовым классом реализации действий является AbstractAction. Пример использования: - - При выполнении метода addAction() реализация ActionsHolder проверяет, нет ли уже в нем действия с таким же идентификатором. Если есть, то имеющееся действие будет заменено на новое переданное. Поэтому можно безопасно, например, декларировать стандартное действие в дескрипторе экрана, а затем в контроллере создать новое с переопределенными методами, и добавить в ActionsHolder. - Кроме создания наследников AbstractAction или стандартных действий, возможно конфигурирование декларативно созданных действий путем получения их в контроллере и установки атрибутов. Например: - - При этом во всех визуальных компонентах, связанных с данным действием, значение соответствующего атрибута также изменится. -
-
-
- Aggregation - Механизм задания агрегаций для колонок в Table. - - Атрибуты aggregation - - type − функция агрегирования: - - - SUM − сумма - - - AVG − среднее значение - - - COUNT − количество - - - MIN − минимальное значение - - - MAX − максимальное значение - - - - - formatter − задает формат вывода агрегированного значения. - - - Логика применения форматирования к агрегированному значению следующая: если в элементе aggregation указан formatter, то применяется он; иначе применяется formatter соответствующего агрегированному значению datatype. - - - -
-
- Presentation - TODO -
-
-
- Элементы XML - - - formatter - - XML-элемент formatter задает класс, который будет применен для преобразования значения в строку. - Атрибуты: - - - class − имя класса, реализующего интерфейс com.haulmont.cuba.gui.components.Formatter - - - - - - validator - - XML-элемент для задания механизма валидации значений, введенных в визуальном компоненте. - Атрибуты: - - - script − путь к скрипту Groovy, осуществляющему валидацию - - - class − имя класса Java, реализующего интерфейс Field.Validator - - - message − сообщение, выводимое пользователю в случае ошибки валидации. Атрибут должен содержать ключ сообщения в пакете, например, message="msg://infoTextField.validationMsg" - - - Выбор механизма валидации осуществляется следующим образом: - - - Если не указано значение атрибута script, и сам элемент validator не содержит текста выражения Groovy, то в качестве валидатора используется класс, указанный в атрибуте class. - - - Если элемент validator содержит текст, то он будет использован как выражение Groovy и выполнен с помощью Scripting. - - - В противном случае с помощью Scripting будет выполнен скрипт Groovy, указанный в атрибуте script. - - - В выражение или скрипт Groovy будет передана одна переменная value, содержащая значение, введенное в визуальном компоненте. - Выражение или скрипт должны вернуть boolean значение: true − valid, false − not valid. - CUBA уже содержит несколько реализаций наиболее часто используемых валидаторов (см. пакет com.haulmont.cuba.gui.components.validators), которые можно применять в своих проектах: - - - DateValidator - - - DoubleValidator - - - EmailValidator - - - IntegerValidator - - - LongValidator - - - PatternValidator - - - ScriptValidator - - - Примеры использования: - <field id="imei"> - <validator class="com.haulmont.cuba.gui.components.validators.PatternValidator" - pattern="\d{15}" - message="msg://general.imeiValidationFailed"/> -</field> - -<field id="maxCountOfVisits"> - <validator class="com.haulmont.cuba.gui.components.validators.IntegerValidator"/> -</field> - - - -
-
- Атрибуты XML - - - align - - Атрибут, задающий расположение компонента относительно вышестоящего контейнера. - Возможные значения: - - - TOP_RIGHT - - - TOP_LEFT - - - TOP_CENTER - - - MIDDLE_RIGHT - - - MIDDLE_LEFT - - - MIDDLE_CENTER - - - BOTTOM_RIGHT - - - BOTTOM_LEFT - - - BOTTOM_CENTER - - - - - - caption - - XML-атрибут, устанавливающий заголовок для визуального компонента. - Значением атрибута должна быть либо собственно строка сообщения, либо ключ в пакете сообщений. В случае ключа значение должно начинаться с префикса msg:// - Способы задания ключа: - - - Короткий ключ − при этом сообщение ищется в пакете, заданном для данного экрана: - caption="msg://infoFieldCaption" - - - Полный ключ, с заданием пакета: - caption="msg://com.haulmont.refapp.gui.app/infoFieldCaption" - - - - - - captionProperty - - XML-атрибут визуального компонента, реализующего интерфейс OptionsField. - Задает имя атрибута сущности, которую содержит источник данных, используемый для формирования списка опций (optionsDatasource). - Если данный атрибут не задан, список опций будет содержать Instance Name экземпляров, содержащихся в списке. - - - - clickAction - - Атрибут содержит описание действия, которое будет выполнено при клике в ячейке или в поле (для компонента FieldGroup). Возможны два типа действий: - - - open − открывает для сущности, отображаемой в ячейке, экран редактирования с указанным именем, например: clickAction="open:sec$User.edit". Имя сущности отображается в виде ссылки: -
- - <mediaobject> - <imageobject> - <imagedata contentwidth="40%" align="center" fileref="img/gui_clickAction_open.png"/> - </imageobject> - </mediaobject> - </figure> - </listitem> - <listitem> - <para><code>invoke</code> − вызывает метод <glossterm linkend="screen_controller_glossentry">контроллера</glossterm> экрана с указанным именем, например: <code>clickAction="invoke:onClick"</code>. Метод должен иметь единственный параметр типа <type>Object</type>, в который будет передан экземпляр сущности, отображаемой в ячейке.</para> - </listitem> - </itemizedlist> - </listitem> - </varlistentry> - <varlistentry id="attr_datasource"> - <term>datasource</term> - <listitem> - <para>XML-атрибут компонента, реализующего интерфейс <code>DatasourceComponent</code>.</para> - <para>Предназначен для задания <link linkend="datasources">источника данных</link> и должен содержать имя источника данных, описанного в секции <parameter>dsContext</parameter> <link linkend="screen_xml_glossentry">дескриптора</link> экрана.</para> - <para>Атрибут property является обязательным, иначе возникает ошибка java.lang.IllegalStateException: Can't set assign datasource 'sampleDs' for component 'labelInfo' due 'property' attribute is not defined. </para> - <para>Смотрите также атрибут property.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_description"> - <term>description</term> - <listitem> - <para>Атрибут, задающий текст подсказки для компонента.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_editable"> - <term>editable</term> - <listitem> - <para>XML-атрибут, указывающий на возможность редактирования содержимого (не путать с <link linkend="attr_enable">enable</link>).</para> - <para>Возможные значения − true, false. По умолчанию true.</para> - </listitem> - <listitem>На возможность редактирования содержимого для компонента, связанного с данными (наследника DatasourceComponent или List), влияет также Security. Если по данным security данный компонент должен быть недоступен для редактирования, значение атрибута editable не принимается во внимание.</listitem> - </varlistentry> - <varlistentry id="attr_enable"> - <term>enable</term> - <listitem> - <para>Атрибут компонента, устанавливающий его состояние "enabled/disabled" − доступен/недоступен.</para> - <para>Если компонент недоступен, то он не принимает фокус ввода. Недоступность контейнера приводит к тому, что все его компоненты также становятся недоступными. - -Возможные значения − true, false. - -По умолчанию у всех компонентов enable = true.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_expand"> - <term>expand</term> - <listitem> - <para>Атрибут контейнера для управления его внутренней компоновкой.</para> - <para>Задает компонент внутри контейнера, который необходимо "развернуть", т.е. установить ему максимально возможную высоту и ширину.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_height"> - <term>height</term> - <listitem> - <para>Атрибут, устанавливающий высоту компонента.</para> - <para>Может быть задана в пикселях либо в процентах от высоты вышестоящего контейнера. Например: 100px, 100%, 50. Если единица измерения не указана, подразумевается высота в пикселях.</para> - <para>Простановка значения в % означает, что компонент по высоте займет соответствующую часть пространства, предоставляемого контейнером более высокого уровня.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_icon"> - <term>icon</term> - <listitem> - <para>XML-атрибут, устанавливающий пиктограмму для визуального компонента.</para> - <para>Значением атрибута должен быть путь к файлу пиктограммы относительно каталога темы. Например:</para> - <programlisting>icon="icons/create.png"</programlisting> - <para>Если пиктограмма должна быть выбрана в зависимости от языка пользователя, можно указать путь к ней в пакете сообщений, а в атрибуте icon − ключ сообщения, например:</para> - <programlisting>icon="msg://addIcon"</programlisting> - </listitem> - </varlistentry> - <varlistentry id="attr_id"> - <term>id</term> - <listitem> - <para>Идентификатор компонента.</para> - <para>Возможное значение − любой допустимый Java-идентификатор. Рекомендуется использовать только camelСase, например, <code>userGrid</code>, <code>filterPanel</code>. - -Может быть указан для любого компонента и должен быть уникальным в пределах экрана.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_margin"> - <term>margin</term> - <listitem> - <para>Атрибут margin устанавливает наличие отступа между внешними границами и содержимым контейнера.</para> - <para>Может иметь 2 вида значений:</para> - <itemizedlist> - <listitem> - <para>margin="true" − установить отступ со всех сторон сразу</para> - </listitem> - <listitem> - <para>margin="true;false;true;false;" − установить отступ только сверху и снизу (формат значения "сверху,справа,снизу,слева")</para> - </listitem> - </itemizedlist> - <para>По умолчанию отступы отсутствуют.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_optionsDatasource"> - <term>optionsDatasource</term> - <listitem> - <para>XML-атрибут визуального компонента, реализующего интерфейс <code>OptionsField</code>.</para> - <para>Задает имя <link linkend="datasources">источника данных</link>, используемого для формирования списка опций.</para> - <para>Совместно с optionsDatasource может использоваться атрибут <link linkend="attr_captionProperty">captionProperty</link>.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_property"> - <term>property</term> - <listitem> - <para>XML-атрибут компонента, реализующего интерфейс <code>DatasourceComponent</code>.</para> - <para>Предназначен для задания имени атрибута сущности, значение которого будет отображаться/редактироваться данным визуальным компонентом.</para> - <para>Используется всегда совместно с атрибутом <link linkend="attr_datasource">datasource</link>.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_required"> - <term>required</term> - <listitem> - <para>XML-атрибут визуального компонента, реализующего интерфейс Field. Указывает, что в данное поле обязательно должно быть введено значение.</para> - <para>Возможные значения атрибута − true, false. По умолчанию false.</para> - <para>Совместно с required может использоваться атрибут requiredMessage.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_requiredMessage"> - <term>requiredMessage</term> - <listitem> - <para>XML-атрибут, используемый совместно с атрибутом <link linkend="attr_required">required</link>. Позволяет установить сообщение, выводимое пользователю в случае нарушения требования <link linkend="attr_required">required</link>.</para> - <para>Атрибут должен содержать ключ сообщения в пакете, например: requiredMessage="msg://infoTextField.requiredMessage"</para> - </listitem> - </varlistentry> - <varlistentry id="attr_spacing"> - <term>spacing</term> - <listitem> - <para>Атрибут spacing устанавливает наличие отступов между компонентами внутри контейнера.</para> - <para>Возможные значения − true, false.</para> - <para>По умолчанию отступы отсутствуют.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_stylename"> - <term>stylename</term> - <listitem> - <para>Атрибут, задающий имя стиля компонента.</para> - <para>Для веб-клиента стиль задается в CSS.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_visible"> - <term>visible</term> - <listitem> - <para>Атрибут либо элемент видимости компонента.</para> - <para>Возможные значения − true, false, либо выражение Groovy с boolean-результатом. Если выражение длинное, имеет смысл использовать не атрибут, а элемент visible внутри текущего компонента − эффект тот же. Если контейнер невидим, не видны и все его компоненты. По умолчанию все компоненты видимы.</para> - </listitem> - </varlistentry> - <varlistentry id="attr_width"> - <term>width</term> - <listitem> - <para>Атрибут, устанавливающий ширину компонента.</para> - <para>Значение может быть задано в пикселях или в процентах от ширины вышестоящего контейнера. Например: 100px, 100%, 50. Если единица измерения не указана, подразумевается ширина в пикселях. Простановка значения в % означает, что компонент по ширине займет соответствующую часть пространства, предоставляемого контейнером более высокого уровня.</para> - </listitem> - </varlistentry> - </variablelist> - </section> - <section id="gui_themes"> - <title>Создание темы приложения - TODO -
-
-
- Источники данных - Предназначены для реализации связанных с данными (data-aware) компонентов. - Существуют следующие интерфейсы источников данных: - - - Datasource − предназначен для работы с одним экземпляром сущности. - - - RuntimePropsDatasource − предназначен для работы с динамическими атрибутами сущностей. - - - - - CollectionDatasource − предназначен для работы с коллекцией сущностей - - - HierarchicalDatasource − предназначен для работы с компонентами - Tree - , - TreeTable - . - - - GroupDatasource − предназначен для работы с компонентом - GroupTable - . - - - - - Как правило, источники данных объявляются декларативно в секции dsContext дескриптора экрана. -
-
- Фоновые задачи - Предназначение - Фоновые задачи используются на клиентском уровне для выполнения длительных процессов без заморозки пользовательского интерфейса. - В платформе реализован объект BackgroundWorker, который предоставляется окружением и управляет фоновыми задачами - BackgroundWorker backgroundWorker = AppConfig.getBackgroundWorker(); - Использование - - - Задача описывается как наследник абстрактного класса BackgroundTask. Для нее необходимо задать окно, которому принадлежит задача, и описать главный метод run(). - - - Создается объект управления задачей − BackgroundTaskHandler. Для этого задачу необходимо передать классу BackgroundWorker. - - - Выполняется запуск задачи - // Задача с ограничением 10 секунд и с текущим окном в качестве родителя -final BackgroundTask<Integer, Void> progressIndicator = new BackgroundTask<Integer, Void>(10, this) { - @Override - public Void run(TaskLifeCycle<T> taskLifeCycle) { - // 1 2 3 4 5 :-) - for (int i = 0; i < 5; i++) { - Long res; - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException ignored) { - return null; - } - // Оглашаем прогресс - taskLifeCycle.publish(i); - } - return null; - } - - @Override - public void canceled() { - // отменено - } - - @Override - public void done(Void result) { - // завершено - } - - @Override - public void progress(List<Integer> changes) { - // индикация прогресса - } - - @Override - public Map<String, Object> getParams() { - // передаём параметры - return Collections.emptyMap(); - } -}; - -// Получение управляющего объекта и запуск -BackgroundTaskHandler taskHandler = backgroundWorker.handle(progressIndicator); -taskHandler.execute(); - - - Объект задачи - BackgroundTask<T, V> − параметризованный класс: - - - T − тип объектов, показывающих прогресс задачи. Они передаются в метод progress() при вызове publish() в рабочем потоке - - - V − тип результата задачи, его можно получить после выполнения задачи или вызвать синхронно getResult() для ожидания. - - - Метод canceled() вызывается только в случае управляемой отмены задачи (то есть при вызове cancel() у TaskHandler). - Если у задачи истек таймаут, или было закрыто окно, в котором она исполнялась, то задача будет завершена без уведомлений. - - Следует помнить, что в Java невозможно прервать поток, если он не использует операций, выбрасывающих InterrruptedException. Никогда не перехватывайте это исключение или все исключения с целью тихо завершить операцию. Хорошим тоном является проверка флага isInterrupted() у объекта TaskLifeCycle в различных циклических операциях, для того чтобы вовремя отменить выполнение при прерывании задачи. - - Объекты BackgroundTask не имеют состояния. Если придерживаться этого подхода и не заводить полей для хранения промежуточных данных, то можно использовать множество параллельно работающих задач, используя всего один объект задачи. - Объект BackgroundHandler можно запускать всего один раз; если требуется частый перезапуск задач, то используйте BackgroundTaskWrapper - Отображение фоновых действий для пользователя - Иногда необходимо показывать пользователю окно с прогрессом и кнопкой Отмена. Для этого есть BackgroundWorkWindow<T,V> с набором статических методов. -В окне можно отображать статус задачи и разрешать/запрещать отмену фонового процесса. - Отслеживание исполнения задач - Если Вы хотите использовать параметры из UI компонентов, то необходимо переопределить метод Map<String, Object> getParams() . Он выполняется один раз при запуске задачи в потоке UI. В методе run они доступны в объекте TaskLifeCycle, аксессор − getParams(). - При возникновении исключительных ситуаций вызывается метод handleException, в котором можно отобразить ошибку на UI. - Для отмены и удаления зависших задач предусмотрены следующие меры: - - - WatchDog − поток, постоянно проверяющий задачи на истечение таймаута. Зависшие задачи прерываются и удаляются из обработки - - - При закрытии родительского окна задачи она прерывается - - - По истечению сессии пользователя все его задачи прерываются. -Для этого в web.xml указать: - <listener> - <listener-class>com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener</listener-class> -</listener> - - - Объявление WatchDog - В app-web-spring.xml и app-desktop-spring.xml добавить объявление задачи по расписанию: - <bean id="backgroundWorkerScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"> - <property name="daemon" value="true"/> - <property name="poolSize" value="1"/> -</bean> - -<task:scheduled-tasks scheduler="backgroundWorkerScheduler"> - <task:scheduled ref="cuba_BackgroundWorker_WatchDog" method="cleanupTasks" fixed-delay="2000"/> -</task:scheduled-tasks> - Настройки - Для Web слоя в WebConfig настраивается частота проверки изменений на стороне клиента (браузера): cuba.backgroundWorker.uiCheckInterval (По умолчанию 2000 мс) -
-
- Таймеры - TODO -
-
-
- Компоненты портала - TODO -
-
- Механизмы платформы -
- Выполнение задач по расписанию - TODO -
-
- Отсылка email - TODO -
-
- Динамические атрибуты - TODO -
-
- Пессимистичная блокировка - TODO -
-
- Статистика сущностей - TODO -
-
- Аудит изменений сущностей - TODO -
-
- Снимки сущностей - TODO -
-
- REST API - TODO -
-
- Хранилище файлов - TODO -
-
- Организация кэшей данных - TODO -
-
- Выполнение SQL с помощью QueryRunner - TODO -
-
- Интеграция с MyBatis - TODO -
-
- Панель папок -
- Папки поиска - Папки поиска обладают следующими особенностями: - - - при выборе отображают экраны с универсальным фильтром; - - - создаются пользователем из любого отображенного экрана с универсальным фильтром; - - - пользователь может изменить иерархию, название, либо переопределить фильтр поиска. - - -
-
- Папки приложения - Обладают следующими особенностями: - - - при выборе отображают предопределенные экраны с фильтром или без; - - - набор папок может зависеть от ролей пользователя; - - - пользователь не может изменить настройки папок; - - - в заголовке папки может отображаться текущее количество входящих в нее записей; - - - периодически обновляются по таймеру. - - -
-
-
- Инспектор сущностей - TODO -
-
- Информация об используемом ПО - TODO -
-
-
- Расширение функциональности - TODO -
- Расширение сущностей - TODO -
-
- + + + + Устройство платформы +
+ Архитектура + В данной главе рассмотрена архитектура CUBA-приложений в различных разрезах: по уровням, блокам, модулям, и по используемым базовым проектам. +
+ Уровни и блоки приложения + Платформа позволяет строить приложения по классической трехуровневой схеме: клиентский уровень, средний слой, база данных. Уровень отражает степень "удаленности" пользователя от хранимых данных. + В дальнейшем речь пойдет в основном о среднем слое и клиентах, поэтому для краткости выражение "все уровни" означает два этих уровня. + На каждом уровне возможно создание одного или нескольких блоков (units) приложения. Блок представляет собой обособленную исполняемую программу, взаимодействующую с другими блоками приложения. Средства платформы CUBA позволяют создавать блоки в виде веб-приложений и десктопных приложений. Разработка блоков для мобильных платформ на данный момент остается за рамками CUBA, однако такие блоки, созданные другими средствами, могут быть интегрированы со стандартными блоками приложения. +
+ Уровни и блоки приложения + + + + + +
+ + + Middleware + + Средний слой, содержащий основную бизнес-логику приложения и выполняющий обращения к базе данных. Представляет собой отдельное веб-приложение под управлением стандартного контейнера Java EE Web Profile. См. + + + + Web Client + + Основной блок клиентского уровня. Содержит интерфейс, предназначенный, как правило, для внутренних пользователей организации. Представляет собой отдельное веб-приложение под управлением стандартного контейнера Java EE Web Profile. Реализация пользовательского интерфейса основана на фреймворке Vaadin. См. + + + + Desktop Client + + Дополнительный блок клиентского уровня. Содержит интерфейс, предназначенный, как правило, для внутренних пользователей организации. Представляет собой десктопное Java-приложение, реализация пользовательского интерфейса основана на фреймворке Java Swing. См. + + + + Web Portal + + Дополнительный блок клиентского уровня. Содержит интерфейс для внешних пользователей. Может использоваться для интеграции с мобильными устройствами или сторонними приложениями. Представляет собой отдельное веб-приложение под управлением стандартного контейнера Java EE Web Profile. Реализация пользовательского интерфейса основана на фреймворке Spring MVC. См. + + + + Обязательным блоком любого приложения является средний слой - Middleware. Для реализации пользовательского интерфейса как правило используется один или несколько клиентских блоков, например Web Client и Web Portal. + Вышеперечисленные блоки являются стандартными, однако в комплексном приложении для разделения функциональности можно без труда создать произвольное количество как клиентских блоков, так и блоков среднего слоя. + Все клиентские блоки взаимодействуют со средним слоем одинаковым образом посредством протокола HTTP, что позволяет размещать средний слой произвольным образом, в том числе за сетевым экраном. Следует отметить, что при развертывании в простейшем случае среднего слоя и веб-клиента на одном сервере между ними организуется локальное взаимодействие в обход сетевого стека для снижения накладных расходов. +
+
+ Модули приложения + Модуль – наименьшая структурная единица CUBA-приложения. Представляет собой один модуль проекта приложения и соответствующий ему JAR файл с исполняемым кодом. + Стандартные модули: + + global – включает в себя классы сущностей, интерфейсы сервисов и другие общие для всех уровней классы. Используется во всех блоках приложения. + + + core – реализация сервисов и всех остальных компонентов среднего слоя. Используется только на Middleware. + + + gui – общие компоненты универсального пользовательского интерфейса. Используется в Web Client и Desktop Client. + + + web – реализация универсального пользовательского интерфейса на Vaadin, а также другие специфичные для веб-клиента классы. Используется в блоке Web Client. + + + desktop – опциональный модуль – реализация универсального пользовательского интерфейса на Java Swing, а также другие специфичные для десктоп-клиента классы. Используется в блоке Desktop Client. + + + portal – опциональный модуль – реализация веб-портала на Spring MVC. + + +
+ Модули приложения + + + + + +
+
+
+ Базовые проекты + Функциональность платформы разделена на несколько так называемых базовых проектов: + + cuba – основной базовый проект, содержит всю функциональность, описанную в данном руководстве, плюс подсистему безопасности (управление пользователями и их доступом к данным) + + + reports – подсистема генерации отчетов + + + workflow – подсистема управления потоками работ со встроенным визуальным редактором бизнес-процессов + + + fts – подсистема полнотекстового поиска + + + charts – подсистема вывода диаграмм + + + ccpayments – подсистема работы с кредитными картами + + + bpmn – механизм исполнения бизнес-процессов по стандарту BPMN 2.0 + + + Создаваемое на основе платформы приложение может включать в себя функциональность базовых проектов путем объявления зависимостей от их артефактов. Зависимость от артефактов cuba является обязательной. Опциональные базовые проекты в свою очередь также зависят от cuba, и в принципе могут содержать зависимости между собой. +
+ Зависимости между проектами + + + + + +
+ Сплошными линиями изображены обязательные зависимости, пунктирными − опциональные. +
+
+ Состав приложения + Вышеописанные архитектурные принципы напрямую отражаются на составе собранного приложения. Рассмотрим его на примере простого приложения sales, которое имеет 2 блока – Middleware и Web Client; и включает в себя функциональность базовых проектов cuba и reports. +
+ Состав простого приложения + + + + + +
+ На рисунке изображено содержимое некоторых каталогов сервера Tomcat с развернутым в нем приложением sales. + Блок Middleware реализован веб-приложением app-core, блок Web Client – веб-приложением app. Исполняемый код веб-приложений содержится в каталогах WEB-INF/lib в наборе JAR-файлов. Каждый JAR представляет собой результат сборки (артефакт) одного из модулей приложения или базового проекта. + Например, состав JAR-файлов веб-приложения среднего слоя app-core определяется тем, что блок Middleware состоит из модулей global и core, и приложение использует базовые проекты cuba и reports (в данном случае версии 4.0.0). +
+
+
+ Общие компоненты + В данной главе рассмотрены компоненты платформы, общие для всех уровней приложения. +
+ Модель данных + Предметная область моделируется в приложении с помощью взаимосвязанных классов Java, называемых классами сущностей или просто сущностями. + Сущности подразделяются на две категории: + + персистентные – экземпляры таких сущностей хранятся в таблицах базы данных + + + неперсистентные – экземпляры существуют только в оперативной памяти + + + Сущности характеризуются своими атрибутами. Атрибут соответствует полю класса и паре методов доступа (get / set) к полю. Чтобы атрибут был неизменяемым (read only), достаточно не создавать метод set. + Персистентные сущности могут включать в себя атрибуты, не хранящиеся в БД. В случае неперсистентного атрибута можно не создавать поле класса, ограничившись методами доступа. + Класс сущности должен удовлетворять следующим требованиям: + + наследоваться от одного из базовых классов, предоставляемых платформой (см. ниже) + + + иметь набор полей и методов доступа, соответствующих атрибутам сущностей + + + класс и его поля (или методы доступа при отсутствии для атрибута соответствующего поля) должны быть определенным образом аннотированы для работы JPA (в случае персистентной сущности) и фреймворка метаданных + + + для поддержки возможного расширения сущностей поля класса необходимо объявлять с модификатором protected, а не private + + + Поддерживаются следующие типы атрибутов сущностей: + + java.lang.String + + + java.lang.Boolean + + + java.lang.Integer + + + java.lang.Long + + + java.lang.Double + + + java.math.BigDecimal + + + java.util.Date + + + java.sql.Date + + + java.sql.Time + + + java.util.UUID + + + byte[] + + + enum + + + сущность + + +
+ Базовые классы сущностей + Рассмотрим базовые классы и интерфейсы сущностей более подробно. +
+ Базовые классы сущностей + + + + + +
+ + + Instance – декларирует базовые методы работы с объектами предметной области: + + получение ссылки на мета-класс объекта + + + генерация имени экземпляра + + + чтение/установка значений атрибутов по имени + + + добавление слушателей, получающих уведомления об изменениях атрибутов + + + + + Entity – дополняет Instance понятием идентификатора сущности, причем Entity не определяет тип идентификатора, оставляя эту возможность наследникам + + + AbstractInstance – реализует логику работы со слушателями изменения атрибутов + + AbstractInstance хранит слушателей в коллекции WeakReference, т.е. при отсутствии внешних ссылок на добавленного слушателя, он будет немедленно уничтожен сборщиком мусора. Как правило, слушателями изменения атрибутов являются визуальные компоненты и источники данных UI, на которые всегда имеются ссылки из других объектов, поэтому проблема исчезновения слушателей не возникает. Однако если слушатель создается прикладным кодом и на него никто не ссылается естественным образом, необходимо кроме добавления в Instance сохранить его в некотором поле объекта. + + + + AbstractNotPersistentEntity – базовый класс неперсистентных сущностей с идентификаторами типа UUID. + + + BaseEntity – декларирует присущие всем персистентным сущностям методы работы с информацией о том, кто и когда создал экземпляр сущности в базе данных + + + BaseUuidEntity - реализует BaseEntity с типом идентификатора UUID и поддержкой JPA + + + Versioned – интерфейс сущностей, поддерживающих оптимистичную блокировку + + + Updatable – интерфейс сущностей, для которых требуется сохранять информацию о том, кто и когда изменял экземпляр в последний раз + + + SoftDelete – интерфейс сущностей, поддерживающих мягкое удаление + + + StandardEntity – наиболее часто используемый базовый класс персистентных сущностей, реализующий вышеперечисленные интерфейсы + + + При создании классов сущностей рекомендуется выбирать базовый класс по следующим правилам: + + если сущность не хранится в БД, наследуйте ее от AbstractNotPersistentEntity + + + если сущность встраиваемая - наследуйте ее от EmbeddableEntity + + + если сущность только создается в БД, никогда не изменяется, и мягкое удаление не требуется - наследуйте ее от BaseUuidEntity + + + если сущность ведет себя стандартным образом: изменяется в БД, требует оптимистичной блокировки и мягкого удаления − наследуйте ее от StandardEntity + + + в противном случае наследуйте сущность от BaseUuidEntity и реализуйте в классе тот набор интерфейсов Versioned, Updatable, SoftDelete, который требуется + + + +
+
+ Аннотации сущностей + В данном разделе описаны все поддерживаемые платформой аннотации классов и атрибутов сущностей. + Аннотации пакета javax.persistence обеспечивают работу JPA, аннотации пакетов com.haulmont.* предназначены для управления метаданными и другими механизмами платформы. + Если для аннотации указано только простое имя класса, подразумевается что это класс платформы, расположенный в одном из пакетов com.haulmont.* +
+ Аннотации класса + + + + + @javax.persistence.Entity + + + + Объявляет класс сущностью модели данных. + Параметры: + + name - имя сущности, обязательно должно начинаться с префикса, отделенного знаком $. Желательно использовать в качестве префикса короткое имя проекта для формирования отдельного пространства имен. + + + Пример:@Entity(name = "sales$Customer") + + + + + + @javax.persistence.MappedSuperclass + + + + Определяет, что данный класс является предком некоторых сущностей, и его атрибуты должны быть использованы в составе сущностей-наследников. Такой класс не сопоставляется никакой отдельной таблице БД. + + + + + @javax.persistence.Table + + + Определяет таблицу базы данных для данной сущности. + Параметры: + + name - имя таблицы + + + Пример:@Table(name = "SALES_CUSTOMER") + + + + + + @javax.persistence.Embeddable + + + + Определяет встраиваемую сущность, экземпляры которой хранятся вместе с владеющей сущностью в той же таблице. + Для задания имени сущности тебуется применение аннотации + @MetaClass + . + + + + + + @javax.persistence.Inheritance + + + + Определяет стратегию наследования для иерархии классов сущностей. Данная аннотация должна быть помещена на корневом классе иерархии. + Параметры: + + strategy - стратегия, по умолчанию SINGLE_TABLE + + + + + + + @javax.persistence.DiscriminatorColumn + + + Используется для определения колонки БД, отвечающей за различение типов сущностей в случае стратегий наследования SINGLE_TABLE и JOINED. + Параметры: + + name - имя колонки-дискриминатора + + + discriminatorType - тип данных колонки-дискриминатора + + + Пример:@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER) + + + + + + @javax.persistence.DiscriminatorValue + + + + Определяет значение колонки-дискриминатора для данной сущности. Эта аннотация должна быть помещена на конкретном классе сущности. + Пример:@DiscriminatorValue("0") + + + + + + @javax.persistence.PrimaryKeyJoinColumn + + + + Используется в случае стратегии наследования JOINED для указания колонки внешнего ключа данной сущности, ссылающегося на первичный ключ сущности-предка. + Параметры: + + name - имя колонки внешнего ключа данной сущности + + + referencedColumnName - имя колонки первичного ключа сущности предка + + + Пример:@PrimaryKeyJoinColumn(name = "CARD_ID", referencedColumnName = "ID") + + + + + @NamePattern + + + Определяет способ получения имени экземпляра, возвращаемого методом Instance.getInstanceName(). + Значением аннотации должна быть строка вида {0}|{1}, где + + {0} - строка форматирования по правилам String.format(), или имя метода данного объекта с префиксом #. Метод должен возвращать String и не иметь параметров. + + + {1} - разделенный запятыми список имен полей класса, соответствующий формату {0}. В случае использования в {0} метода список полей все равно необходим, так как по нему формируется представление _minimal. + + + Примеры:@NamePattern("%s|name")@NamePattern("#getCaption|login,name") + + + + + @Listeners + + + Определяет список слушателей, предназначенных для реакции на события жизненного цикла экземпляров сущности на уровне Middleware. + Значением аннотации должна быть строка или массив строк с именами классов слушателей - см. + Строки используются здесь вместо ссылок на классы потому, что классы слушателей находятся только на уровне Middleware и не доступны клиентскому коду, в то время как классы самих сущностей используются на всех уровнях. + Примеры:@Listeners("com.haulmont.cuba.security.listener.UserEntityListener")@Listeners({"com.abc.sales.entity.FooListener","com.abc.sales.entity.BarListener"}) + + + + + @MetaClass + + + Используется для объявления неперсистентной или встраиваемой сущности (т.е. когда аннотация @javax.persistence.Entity не применима) + Параметры: + + name - имя сущности, обязательно должно начинаться с префикса, отделенного знаком $. Желательно использовать в качестве префикса короткое имя проекта для формирования отдельного пространства имен. + + + Пример:@MetaClass(name = "sys$LockInfo") + + + + + @SystemLevel + + + Указывает, что данная сущность является системной и не должна быть доступна для выбора пользователем в различных списках сущностей, например как тип параметра универсального фильтра или тип динамического атрибута. + + + + + @EnableRestore + + + Указывает, что экземпляры данной сущности доступны для восстановления после мягкого удаления в специальном экране core$Entity.restore. + + + + + @TrackEditScreenHistory + + + Указывает, что для данной сущности будет запоминаться история открытия экранов редактирования ({имя_сущности}.edit) с возможностью отображения в специальном экране sec$ScreenHistory.browse. + + + + + @Extends + + + Указывает, что данная сущность является расширением и должна повсеместно использоваться вместо базовой. См. + + + +
+
+ Аннотации атрибутов + Аннотации атрибутов устанавливаются на соответствующие поля класса, за одним исключением: если требуется объявить неизменяемый (read only) неперсистентный атрибут foo, то достаточно создать метод доступа getFoo() и поместить на этот метод аннотацию @MetaProperty. + + + + + @javax.persistence.Transient + + + + Указывает, что данное поле не хранится в БД, т.е. является неперсистентным. + Поля поддерживаемых JPA типов (см. http://docs.oracle.com/javaee/5/api/javax/persistence/Basic.html) по умолчанию являются персистентными, поэтому аннотация @Transient обязательна для объявления неперсистентного атрибута такого типа. + Для включения @Transient атрибута в метаданные, необходимо также указать аннотацию + @MetaProperty + . + + + + + @org.apache.openjpa.persistence.Persistent + + + Указывает, что данное поле хранится в БД, т.е. является персистентным. + Данная аннотация требуется только для нестандартного для JPA типа поля, платформа на данный момент поддерживает один такой тип - java.util.UUID. Таким образом, @Persistent требуется только в одном случае - при объявлении персистентного поля типа UUID. + + + + + + @javax.persistence.Column + + + + Определяет колонку БД, в которой будут храниться значения данного атрибута. + Параметры: + + name - имя колонки + + + length - (необязательный параметр, по умолчанию 255) - длина колонки. Используется также при формировании метаданных и в конечном счете может ограничивать максимальныю длину вводимого текста в визуальных компонентах, работающих с данным атрибутом. + + + nullable - (необязательный параметр, по умолчанию true) - может ли атрибут содержать null. При указании nullable = false JPA контролирует наличие значения поля при сохранении, кроме того, визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. + + + + + + + + @javax.persistence.ManyToOne + + + + Определяет атрибут-ссылку на сущность с типом ассоциации много-к-одному. + Параметры: + + fetch - (по умолчанию EAGER) параметр, определяющий, будет ли JPA энергично загружать ассоциированную сущность. Данный параметр всегда должен быть установлен в значение LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. + + + optional - (необязательный параметр, по умолчанию true) - может ли атрибут содержать null. При указании optional = false JPA контролирует наличие ссылки при сохранении, кроме того, визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. + + + Например, несколько экземпляров Order (заказов) ссылаются на один экземпляр Customer (покупателя), в этом случае класс Order должен содержать следующее объявление:@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "CUSTOMER_ID") +protected Customer customer; + + + + + + @javax.persistence.OneToMany + + + + Определяет атрибут-коллекцию ссылок на сущность с типом ассоциации один-ко-многим. + Параметры: + + mappedBy - поле связанной сущности, определяющее ассоциацию + + + targetEntity - тип связанной сущности. Необязательный параметр, если коллекция объявлена с использованием Java generics. + + + fetch - (необязательный параметр, по умолчанию LAZY) - определяет, будет ли JPA энергично загружать коллекцию связанных сущностей. Необходимо всегда оставлять значение по умолчанию LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. + + + cascade - (необязательный параметр, по умолчанию {}) - каскадирование операций определяет, какие операции над сущностью должны быть применены к ассоциированным сущностям. Каскадирование на данном уровне не рекомендуется использовать. + + + Например, несколько экземпляров Item (пунктов заказа) ссылаются на один экземпляр Order (заказ) с помощью @ManyToOne поля Item.order, в этом случае класс Order может содержать коллекцию экземпляров Item:@OneToMany(mappedBy = "order") +protected Set<Item> items; + + + + + + @javax.persistence.OneToOne + + + + Определяет атрибут-ссылку на сущность с типом ассоциации один-к-одному. + Параметры: + + fetch - (по умолчанию EAGER) параметр, определяющий, будет ли JPA энергично загружать ассоциированную сущность. Данный параметр всегда должен быть установлен в значение LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. + + + mappedBy - поле связанной сущности, определяющее ассоциацию. Требуется устанавливать только на ведомой стороне ассоциации. + + + optional - (необязательный параметр, по умолчанию true) - может ли атрибут содержать null. При указании optional = false JPA контролирует наличие ссылки при сохранении, кроме того, визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. + + + Пример ведущей стороны ассоциации, класс Driver:@OneToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "CALLSIGN_ID") +protected DriverCallsign callsign; + Пример ведомой стороны ассоциации, класс DriverCallsign:@OneToOne(fetch = FetchType.LAZY, mappedBy = "callsign") +protected Driver driver; + + + + + + @javax.persistence.ManyToMany + + + + Определяет атрибут-коллекцию ссылок на сущность с типом ассоциации много-ко-многим. + Ассоциация много-ко-многим всегда имеет ведущую сторону и может иметь обратную сторону - ведомую. На ведущей строне указывается дополнительная аннотация @JoinTable, на ведомой стороне - параметр mappedBy. + Параметры: + + mappedBy - поле связанной сущности, определяющее ассоциацию с ведущей стороны. Необходимо указывать только на ведомой стороне. + + + targetEntity - тип связанной сущности. Необязательный параметр, если коллекция объявлена с использованием Java generics. + + + fetch - (необязательный параметр, по умолчанию LAZY) - определяет, будет ли JPA энергично загружать коллекцию связанных сущностей. Необходимо всегда оставлять значение по умолчанию LAZY, так как в CUBA-приложении политика загрузки связей определяется динамически на основе механизма представлений. + + + Пример:TODO + + + + + + @javax.persistence.JoinColumn + + + + Используется для указания колонки БД, определяющей ассоциацию между сущностями. + Параметры: + + name - имя колонки + + + Пример:@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "CUSTOMER_ID") +protected Customer customer; + + + + + + @javax.persistence.JoinTable + + + + Используется для указания таблицы связи на ведущей стороне @ManyToMany ассоциации. + Параметры: + + name - имя таблицы связи + + + joinColumns - элемент @JoinColumn, определяющий колонку таблицы связей, соответствующую первичному ключу ведущей стороны ассоциации (т.е. содержащей аннотацию @JoinTable) + + + inverseJoinColumns - элемент @JoinColumn, определяющий колонку таблицы связей, соответствующую первичному ключу ведомой стороны ассоциации + + + Пример атрибута customers класса Group, являющегося ведущей стороной ассоциации:@ManyToMany +@JoinTable(name = "SALES_CUSTOMER_GROUP_LINK", + joinColumns = @JoinColumn(name = "GROUP_ID"), + inverseJoinColumns = @JoinColumn(name = "CUSTOMER_ID")) +protected Set<Customer> customers; + Пример атрибута groups класса Customer, являющегося ведомой стороной этой же ассоциации:@ManyToMany(mappedBy = "customers") +protected Set<Group> groups; + + + + + + @javax.persistence.OrderBy + + + + Определяет порядок элементов в атрибуте-коллекции на момент извлечения из базы данных. Данную аннотацию необходимо задавать для упорядоченных коллекций, таких как List или LinkedHashSet для получения предсказуемого порядка следования элементов. + Параметры: + + value - строка, определяющая порядок, в формате:orderby_list::= orderby_item [,orderby_item]* +orderby_item::= property_or_field_name [ASC | DESC] + + + Пример:@OneToMany(mappedBy = "user") +@OrderBy("createTs") +protected List<UserRole> userRoles; + + + + + + @javax.persistence.Embedded + + + + Определяет атрибут типа встраиваемой сущности, в свою очередь аннотированной @Embeddable. + Пример:@Embedded +protected Address address; + + + + + + @javax.persistence.Temporal + + + + Для атрибута типа java.util.Date уточняет тип хранимого значения: дата, время или дата+время. + Параметры: + + value - тип хранимого значения: DATE, TIME, TIMESTAMP + + + Пример:@Column(name = "START_DATE") +@Temporal(TemporalType.DATE) +protected Date startDate; + + + + + + @javax.persistence.Version + + + + Указывает, что данное поле хранит версию для поддержки оптимистичной блокировки сущностей. + Применение такого поля необходимо при реализации классом сущности интерфейса Versioned (базовый класс StandardEntity уже содержит такое поле). + Пример:@Version +@Column(name = "VERSION") +private Integer version; + + + + + @MetaProperty + + + Указывает, что данный атрибут должен быть включен в метаданные. Данная аннотация может быть установлена как на поле класса, так и на метод доступа, в случае отсутствия соответствующего атрибуту поля. + Данная аннотация не обязательна для полей, снабженных следующими аннотациями пакета javax.persistence: @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded. Такие поля отражаются в метаданных автоматически. Поэтому @MetaProperty в основном применяется для определения неперсистентных атрибутов сущностей. + Параметры: + + mandatory - (необязательный параметр, по умолчанию false) - может ли атрибут содержать null. При указании mandatory = true визуальные компоненты, работающих с данным атрибутом, могут сигнализировать пользователю о необходимости ввода значения. + + + Пример использования для поля:@Transient +@MetaProperty +protected String token; + Пример использования для метода:@MetaProperty +public String getLocValue() { + if (!StringUtils.isBlank(messagesPack)) { + return AppBeans.get(Messsages.class).getMessage(messagesPack, value); + } else { + return value; + } +} + + + + + @OnDelete + + + Определяет политику обработки связи в случае мягкого удаления сущности, содержащей данный атрибут. См. + Пример:@OneToMany(mappedBy = "group") +@OnDelete(DeletePolicy.CASCADE) +private Set<Constraint> constraints; + + + + + @OnDeleteInverse + + + Определяет политику обработки связи в случае мягкого удаления сущности с обратной стороны ассоциации. См. + Пример:@ManyToOne +@JoinColumn(name = "DRIVER_ID") +@OnDeleteInverse(DeletePolicy.DENY) +private Driver driver; + + + + + @Composition + + + Указывает на то, что связь является композицией - более тесным вариантом ассоциации. Это означает, что связанная сущность имеет смысл только как часть владеющей сущности, т.е. создается и удаляется вместе с ней. + Например список пунктов в заказе (класс Order содержит коллекцию экземпляров Item):@OneToMany(mappedBy = "order") +@Composition +protected List<Item> items; + + + + + @LocalizedValue + + + Служит для описания способа получения локализованного значения некоторого изменяющегося атрибута, которое возвращает метод MessageTools.getLocValue(). + Параметры: + + messagePack - явное указание пакета, из которого будет взято локализованное сообщение, например com.haulmont.cuba.core.entity + + + messagePackExpr - выражение в терминах пути к атрибуту, хранящему имя пакета, из которого будет взято локализованное сообщение, например proc.messagesPack. Путь начинается с атрибута текущей сущности. + + + Пример аннотации, означающей, что локализованное значение атрибута state будет взято из пакета, имя которого хранится в атрибуте messagesPack связанной сущности proc:@Column(name = "STATE") +@LocalizedValue(messagePackExpr = "proc.messagesPack") +protected String state; + +@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "PROC_ID") +protected Proc proc; + + + +
+
+
+ Атрибуты типа enum + В стандартном варианте использования JPA для атрибутов типа enum в базе данных хранится целое число, получаемое методом ordinal() этого перечисления. Такой подход может привести к следующим проблемам при эксплуатации и развитии системы: + + при появлении в БД значения, не равного ни одному ordinal значению перечисления, экземпляр сущности нельзя загрузить вообще; + + + невозможно ввести новое значение между имеющимися, что актуально при использовании сортировки по значению перечисления (order by). + + + Чтобы решить эти проблемы, в подходе CUBA предлагается отвязать значение, хранимое в БД, от ordinal перечисления. Для этого необходимо поле класса сущности объявлять с типом, хранимым в БД (Integer или String), а методы доступа (getter / setter) создавать для типа самого перечисления. + Например:@Entity(name = "sales$Customer") +@Table(name = "SALES_CUSTOMER") +public class Customer extends StandardEntity { + + @Column(name = "GRADE") + protected Integer grade; + + public CustomerGrade getGrade() { + return grade == null ? null : CustomerGrade.fromId(grade); + } + + public void setGrade(CustomerGrade grade) { + this.grade = grade == null ? null : grade.getId(); + } +... +} + При этом сам класс перечисления может выглядеть следующим образом:public enum CustomerGrade implements EnumClass<Integer> { + + PREMIUM(10), + HIGH(20), + MEDIUM(30); + + private Integer id; + + CustomerGrade(Integer id) { + this.id = id; + } + + @Override + public Integer getId() { + return id; + } + + public static CustomerGrade fromId(Integer id) { + for (CustomerGrade grade : CustomerGrade.values()) { + if (grade.getId().equals(id)) + return grade; + } + return null; + } +} + Для правильного отражения в метаданных класс перечисления, используемый в качестве типа атрибута сущности, должен реализовывать интерфейс EnumClass. + Как видно из примеров, для атрибута grade в БД хранится значение типа Integer, задаваемое полем id перечисления CustomerGrade, а конкретно 10, 20 или 30. В то же время прикладной код и метаданные работают с самим типом CustomerGrade через методы доступа, которые и осуществляют конвертацию. + При наличии в поле БД значения, не соответствующего ни одному значению перечисления, метод getGrade() просто вернет null. Для ввода нового значения, например HIGHER, между HIGH и PREMIUM, достаточно добавить это значение в перечисление с идентификатором 15, при этом сортировка по полю Customer.grade останется верной. +
+
+ Мягкое удаление + Платформа CUBA поддерживает режим "мягкого удаления" данных - когда вместо удаления записей из базы данных они только помечаются определенным образом и становятся недоступными для обычного использования. В дальнейшем такие записи можно либо совсем удалить из БД с помощью отдельной регламентной процедуры, либо восстановить. + Механизм мягкого удаления является "прозрачным" для прикладного программиста - достаточно убедиться что класс сущности реализует интерфейс SoftDelete, и платформа сама нужным образом будет модифицировать запросы и операции с данными. + Режим мягкого удаления имеет следующие преимущества: + + значительно снижается риск потери данных вследствие неверных действий пользователей + + + позволяет быстро сделать некоторые записи недоступными, даже если на них имеются ссылки. + Возьмем для примера модель данных Заказы - Покупатели. Допустим, на некоторого покупателя оформлено несколько заказов, однако нам нужно сделать его недоступным для дальнейшей работы. Традиционным "жестким" удалением сделать это невозможно, так как для удаления покупателя нам нужно либо удалить все его заказы, либо обнулить в этих заказах ссылки на него (т.е. потерять информацию). При мягком удалении покупателя он становится недоступным для поиска и изменения, однако при просмотре заказов пользователь видит на экране название покупателя, так как при загрузке связей признак удаления намеренно игнорируется. + Описанное поведение является стандартным, но может быть модифицировано с помощью политики обработки связей при удалении. + + + Отрицательной стороной мягкого удаления является увеличение объема базы данных и потенциальная необходимость дополнительных процедур ее очистки. +
+ Использование + Для того, чтобы экземпляры сущности удалялись мягко, класс сущности должен реализовывать интерфейс SoftDelete, а соответствующая таблица БД должна содержать колонки: + + DELETE_TS - когда удалена запись + + + DELETED_BY - логин пользователя, который удалил запись + + + Поведение системы по умолчанию - сущности, реализующие SoftDelete, удаляются мягко, удаленные сущности не возвращаются запросами и поиском по идентификатору. При необходимости такое поведение можно динамически отключить следующими способами: + + для текущего экземпляра EntityManager - вызовом setSoftDeletion(false) + + + при запросе данных через + DataService + или + DataWorker + - вызовом у передаваемого объекта LoadContext метода setSoftDeletion(false) + + + на уровне источников данных - используя метод CollectionDatasource.setSoftDeletion(false) или атрибут softDeletion="false" элемента collectionDatasource в XML-дескрипторе экрана. + + + В режиме мягкого удаления платформа автоматически отфильтровывает удаленные экземпляры при загрузке по идентификатору и по JPQL-запросу, а также удаленные элементы связанных сущностей в атрибутах-коллекциях. Однако связанные сущности в единичных атрибутах загружаются независимо от того, удален связанный экземпляр или нет. +
+
+ Политика обработки связей + Платформа предоставляет средство обработки связей при удалении сущностей, во многом аналогичное правилам ON DELETE внешних ключей в базе данных. Это средство работает на уровне Middleware и использует аннотации @OnDelete, @OnDeleteInverse атрибутов сущности. + Аннотация @OnDelete обрабатывается при удалении той сущности, в которой она встретилась, а не той, на которую указывает аннотированный атрибут (в этом отличие от каскадных удалений на уровне БД). + + +Аннотация @OnDeleteInverse обрабатывается при удалении той сущности, на которую указывает аннотированный атрибут, (т.е. аналогично каскадному удалению на уровне внешних ключей в БД). Эта аннотация полезна при отсутствии в удаляемом объекте атрибута, который нужно проверять при удалении. При этом, как правило, в проверяемом объекте существует ссылка на удаляемый, на этот атрибут и устанавливается аннотация @OnDeleteInverse. + Значением аннотации может быть: + + DeletePolicy.DENY - запретить удаление сущности, если аннотированный атрибут не null или не пустая коллекция + + + DeletePolicy.CASCADE - каскадно удалить аннотированный атрибут + + + DeletePolicy.UNLINK - разорвать связь с аннотированным атрибутом. Разрыв связи имеет смысл указывать только на ведущей стороне ассоциации - той, которая в классе сущности аннотирована @JoinColumn. + + + Примеры: + + Запрет удаления при наличии ссылки: при попытке удаления экземпляра Customer, на который ссылается хотя бы один Order, будет выброшено исключение DeletePolicyException. + Order.java@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "CUSTOMER_ID") +@OnDeleteInverse(DeletePolicy.DENY) +protected Customer customer; + Customer.java@OneToMany(mappedBy = "customer") +protected List<Order> orders; + + + Каскадное удаление элементов коллекции: при удалении экземпляра Role все экземпляры Permission также будут удалены. + Role.java@OneToMany(mappedBy = "role") +@OnDelete(DeletePolicy.CASCADE) +protected Set<Permission> permissions; + Permission.java@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "ROLE_ID") +protected Role role; + + + Разрыв связи с элементами коллекции: удаление экземпляра Role приведет к установке в null ссылок со стороны всех входивших в коллекцию экземпляров Permission. + Role.java@OneToMany(mappedBy = "role") +@OnDelete(DeletePolicy.UNLINK) +protected Set<Permission> permissions; + Permission.java@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "ROLE_ID") +protected Role role; + + + Особенности реализации: + + Нужно быть осторожным при использовании @OnDeleteInverse с политиками CASCADE и UNLINK, так как при этом происходит извлечение из БД на сервер приложения всех экземпляров ссылающихся объектов, изменение и затем сохранение. + Например, в случае ассоциации Customer - Job и большого количества работ для одного заказчика, если поставить на атрибут Job.customer политику @OnDeleteInverse(CASCADE), то при удалении экземпляра заказчика будет предпринята попытка извлечь и изменить все его работы. Это может привести к перегрузке сервера приложения и БД. + С другой стороны, использование @OnDeleteInverse(DENY) безопасно, так как при этом производится только подсчет количества ссылающихся объектов, и если оно больше 0, выбрасывается исключение. Поэтому @OnDeleteInverse(DENY) для атрибута Job.customer вполне допустимо. + + + Политика обработки связей реализуется с помощью Entity Listeners, то есть при сохранении данных в БД на уровне Middleware. + + +
+
+ Ограничение уникальности на уровне БД + В режиме мягкого удаления для ограничения уникальности некоторого значения необходимо обеспечить существование единственной неудаленной записи с этим значением, и произвольного количества удаленных записей с этим же значением. + Реализуется данная логика путем, специфичным для используемого сервера базы данных: + + Если сервер БД поддерживает частичные (partial) индексы (например PostgreSQL), то ограничение уникальности можно создать следующим образом:create unique index IDX_SEC_USER_UNIQ_LOGIN on SEC_USER (LOGIN_LC) where DELETE_TS is null + + + Если сервер БД не поддерживает частичные индексы (например Microsoft SQL Server 2005), то в уникальный индекс можно включить поле DELETE_TS:create unique index IDX_SEC_USER_UNIQ_LOGIN on SEC_USER (LOGIN_LC, DELETE_TS) + + +
+
+
+ Entity Listeners + Entity Listeners предназначены для реакции на события жизненного цикла экземпляров сущностей на уровне Middleware. + Слушатель представляет собой класс, реализующий один или несколько интерфейсов пакета com.haulmont.cuba.core.listener. Слушатель будет реагировать на события типов, соответствующих реализуемым интерфейсам. + + + + BeforeDetachEntityListener + + + Метод onBeforeDetach() вызывается перед отделением объекта от + EntityManager + при коммите транзакции. + Данный слушатель можно использовать например для заполнения неперсистентных атрибутов сущности перед отправкой ее на клиентский уровень. + + + + + BeforeInsertEntityListener + + + Метод onBeforeInsert() вызывается перед выполнением вставки записи в БД. В данном методе возможны любые операции с текущим + EntityManager + . + + + + + AfterInsertEntityListener + + + Метод onAfterInsert() вызывается после выполнения вставки записи в БД, но до коммита транзакции. В данном методе нельзя модифицировать текущий персистентный контекст, однако можно производить изменения в БД с помощью + QueryRunner + . + + + + + BeforeUpdateEntityListener + + + Метод onBeforeUpdate() вызывается перед изменением записи в БД. В данном методе возможны любые операции с текущим + EntityManager + . + + + + + AfterUpdateEntityListener + + + Метод onAfterUpdate() вызывается после изменения записи в БД, но до коммита транзакции. В данном методе нельзя модифицировать текущий персистентный контекст, однако можно производить изменения в БД с помощью + QueryRunner + . + + + + + BeforeDeleteEntityListener + + + Метод onBeforeDelete() вызывается перед удалением записи из БД (или в случае мягкого удаления - перед изменением записи). В данном методе возможны любые операции с текущим + EntityManager + . + + + + + AfterDeleteEntityListener + + + Метод onAfterDelete() вызывается после удаления записи из БД (или в случае мягкого удаления - после изменения записи), но до коммита транзакции. В данном методе нельзя модифицировать текущий персистентный контекст, однако можно производить изменения в БД с помощью + QueryRunner + . + + + + Entity Listener может быть задан 2-мя способами: + + Статически - имена классов слушателей указываются в аннотации + @Listeners + на классе сущности + + + Динамически - класс сущности и слушателя передаются в метод addListener() бина EntityListenerManager, например: @ManagedBean +public class MyBean implements AppContext.Listener { + + @Inject + private EntityListenerManager entityListenerManager; + + public ClusterManager() { + AppContext.addListener(this); + } + + @Override + public void applicationStarted() { + entityListenerManager.addListener(User.class, MyUserListener.class); + } + + @Override + public void applicationStopped() { + } +} + + + Для всех экземпляров некоторого класса сущности создается и кэшируется один экземпляр слушателя определенного типа, поэтому слушатель не должен иметь состояния. + Если для сущности объявлены несколько слушателей одного типа (например аннотациями класса сущности и его предков, плюс динамически), то их вызов будет выполняться в следующем порядке: + + Для каждого предка начиная с самого дальнего вызываются его динамически добавленные слушатели, затем статически назначенные. + + + После всех предков вызываются динамически добавленные слушатели данного класса, затем статически назначенные. + + +
+
+
+ Metadata Framework + Для эффективной работы с моделью данных в CUBA-приложениях используется фреймворк метаданных, который: + + + предоставляет удобный интерфейс для получения информации о сущностях, их атрибутах и отношениях между сущностями; а также для навигации по ссылкам + + + служит специализированной и более удобной в использовании альтернативой Java Reflection API + + + регламентирует допустимые типы данных и отношений между сущностями + + + позволяет создавать универсальные механизмы работы с данными + + +
+ Интерфейсы метаданных + Рассмотрим основные интерфейсы метаданных. +
+ Интерфейсы фреймворка метаданных + + + + + +
+ + + + Session + + + Точка входа в фреймворк метаданных. Позволяет получать экземпляры MetaClass по имени и по соответствующему классу Java. Обратите внимание на различие методов getClass() и getClassNN() - первые могут возвращать null, вторые нет (NonNull). + Объект Session может быть получен через интерфейс инфраструктуры + Metadata + . + Пример:@Inject +protected Metadata metadata; +... +Session session = metadata.getSession(); +MetaClass metaClass1 = session.getClassNN("sec$User"); +MetaClass metaClass2 = session.getClassNN(User.class); +assert metaClass1 == metaClass2; + + + + + MetaModel + + + Редко используемый интерфейс, служит для группировки мета-классов. + Группировка осуществляется по первым трем частям имени пакета Java класса сущности. Например, мета-класс сущности com.abc.sales.entity.Customer принадлежит мета-модели с именем com.abc.sales + + + + + MetaClass + + + Интерфейс метаданных класса сущности. MetaClass всегда ассоциирован с классом Java, которого он представляет. + Основные методы: + + + getName() – имя сущности, по соглашению первой частью имени до знака $ является код пространства имен, например, sales$Customer + + + getProperties() – список мета-свойств (MetaProperty) + + + getProperty(), getPropertyNN() - получение мета-свойства по имени. Первый метод в случае отсутствия атрибута с указанным именем возвращает null, второй выбрасывает исключение. + Пример:MetaClass userClass = session.getClassNN(User.class); +MetaProperty groupProperty = userClass.getPropertyNN("group"); + + + getPropertyPath() - позволяет перемещаться по ссылкам. Данный метод принимает строковый параметр - путь из имен атрибутов, разделенных точкой. Возвращаемый объект MetaPropertyPath позволяет обратиться к искомому (последнему в пути) атрибуту вызовом getMetaProperty(). + Пример:MetaClass userClass = session.getClassNN(User.class); +MetaProperty groupNameProp = userClass.getPropertyPath("group.name").getMetaProperty(); +assert groupNameProp.getDomain().getName().equals("sec$Group"); + + + getJavaClass() – класс сущности, которому соответствует данный MetaClass + + + getAnnotations() – коллекция мета-аннотаций + + + + + + + MetaProperty + + + Интерфейс метаданных атрибута сущности. + Основные методы: + + + getName() – имя свойства, соответствует имени атрибута сущности + + + getDomain() – мета-класс, которому принадлежит данное свойство + + + getType() – тип свойства: + + простой тип: DATATYPE + + + перечисление: ENUM + + + ссылочный тип двух видов: + + + ASSOCIATION − простая ссылка на другую сущность. Например, отношение заказа и покупателя − ассоциация. + + + COMPOSITION − ссылка на сущность, которая не имеет самостоятельного значения без владеющей сущности. COMPOSITION можно считать "более тесным" отношением, чем ASSOCIATION. Например, отношение заказа и пункта этого заказа − COMPOSITION, т.к. пункт не может существовать без заказа, которому он принадлежит. + + + Вид ссылочного атрибута ASSOCIATION или COMPOSITION влияет на режим редактирования сущности: в первом случае сохранение связанной сущности в базу данных происходит независимо, а во втором − связанная сущность сохраняется в БД только вместе с владеющей сущностью. + + + + + getRange() – интерфейс Range, детально описывающий тип данного атрибута + + + isMandatory() – признак обязательности атрибута. Используется, например, визуальными компонентами для сигнализации пользователю о необходимости ввода значения. + + + isReadOnly() – признак неизменности атрибута + + + getInverse() – для ссылочного атрибута возвращает мета-свойство с обратной стороны ассоциации, если таковое имеется + + + getAnnotatedElement() – поле (java.lang.reflect.Field) или метод (java.lang.reflect.Method), соответствующие данному атрибуту сущности + + + getJavaType() – класс Java данного атрибута сущности. Это либо тип поля класса, либо тип возвращаемого значения метода. + + + getDeclaringClass() – класс Java, содержащий данный атрибут + + + + + + + Range + + + Интерфейс, детально описывающий тип атрибута сущности. + Основные методы: + + + isDatatype() – возвращает true для атрибута простого типа + + + asDatatype() - возвращает + Datatype + для атрибута простого типа + + + isEnum() – возвращает true для атрибута типа перечисления + + + asEnumeration() - возвращает + Enumeration + для атрибута типа перечисления + + + isClass() – возвращает true для ссылочного атрибута типа ASSOCIATION или COMPOSITION + + + asClass() - возвращает мета-класс ассоциированной сущности для ссылочного атрибута + + + isOrdered() – возвращает true если атрибут представляет собой упорядоченную коллекцию (например List) + + + getCardinality() – вид отношения для ссылочного атрибута: ONE_TO_ONE, MANY_TO_ONE, ONE_TO_MANY, MANY_TO_MANY + + + + + +
+
+ Формирование метаданных + Основной источник формирования структуры метаданных - аннотированные классы сущностей. + Класс сущности отражается в метаданные в следующих случаях: + + Класс персистентной сущности аннотирован @Entity, @Embeddable, @MappedSuperclass и зарегистрирован в файле + persistence.xml + + + + Класс неперсистентной сущности аннотирован @MetaClass и зарегистрирован в файле + metadata.xml + + + + Атрибут сущности отражается в метаданных, если: + + поле класса аннотировано @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded + + + поле класса или метод доступа на чтение (getter) аннотирован @MetaProperty + + + Параметры мета-класса и мета-свойств формируются на основе параметров перечисленных аннотаций, а также типов полей и методов класса. Кроме того, если у атрибута отсутствует метод доступа на запись (setter), атрибут становится неизменяемым (read only). + + Внимание + Текущая реализация сборки метаданных имеет следующие ограничения: + + Классы сущностей должны размещаться в пакетах, имеющих не менее 3-х уровней иерархии, например com.abc.sales, com.abc.sales.entity + + + Персистентные сущности не могут ссылаться на неперсистентные. Обратное возможно, т.е. неперсистентная сущность может иметь атрибут типа некоторой персистентной сущности. + + + +
+
+ Datatype + Интерфейс Datatype описывает тип данных, допустимый для атрибута сущности, не являющегося ассоциацией. Каждый экземпляр реализации Datatype соответствует одному классу Java, для работы с которым он предназначен. + Все экземпляры зарегистрированы в репозитории - классе Datatypes, который выполняет загрузку и инициализацию классов реализации Datatype следующим образом: + + в корне CLASSPATH ищется файл + datatypes.xml + , и если он найден, репозиторий Datatypes инициализируется из него + + + в противном случае инициализация Datatypes производится из файла /com/haulmont/chile/core/datatypes/datatypes.xml + + + Экземпляр Datatype может быть получен двумя способами: + + из мета-свойства типа + DATATYPE + , вызовом getRange().asDatatype() + + + статическим методом Datatypes.get(), передавая в него имя реализации Datatype или класс Java, для которого он создан. + + + Основные методы интерфейса Datatype: + + getName() - возвращает уникальное имя данной реализации + + + format() - преобразовывает переданное значение в строку + + + parse() - преобразовывает строку в значение нужного типа + + + Datatype определяет два набора методов для форматирования/парсинга: с учетом локали и без учета локали. Преобразование с учетом локали используется повсеместно в пользовательском интерфейсе, преобразование без учета локали используется в системных механизмах, например для сериализации в REST API. + Форматы для преобразований без учета локали задаются в вышеупомянутом файле + datatypes.xml + . + Форматы для преобразований с учетом локали задаются в главных пакетах локализованных сообщений, в строках со следующими ключами: + + numberDecimalSeparator + + + numberGroupingSeparator + + + integerFormat + + + doubleFormat + + + dateTimeFormat + + + dateFormat + + + timeFormat + + + trueString + + + falseString + + Все перечисленные форматы по умолчанию заданы в главных пакетах локализованных сообщений базового проекта cuba, и могут быть переопределены в аналогичных файлах проекта приложения. +
+ Пример форматирования даты в UI + Рассмотрим отображение атрибута Order.date в таблице браузера заказов. + order-browse.xml<table id="ordersTable"> + ... + <columns> + <column id="date"/> + ... + Атрибут date в классе Order определен с типом "дата":@Column(name = "DATE", nullable = false) +@Temporal(TemporalType.DATE) +private Date date; + Если текущий пользователь зарегистрирован c русской локалью, то из главного пакета локализованных сообщений клиентского уровня, из файла messages_ru.properties извлекается строка:dateFormat=dd.MM.yyyy + Результат: дата "6 августа 2012 года" конвертируется в строку "06.08.2012" для отображения в ячейке таблицы. +
+
+ Примеры форматирования дат и чисел в коде приложения + + + Пример форматирования даты@Inject +protected UserSessionSource userSessionSource; +... +Date date = ...; +String dateStr = Datatypes.get(Date.class).format(date, userSessionSource.getLocale()); + + + Пример форматирования числового значения с повышенной точностью (5 знаков после запятой) в блоке Web Client + /com/abc/sales/web/messages_ru.propertiescoordinateFormat = #,##0.00000 + SomeClass.java@Inject +protected Messages messages; +@Inject +protected UserSessionSource userSessionSource; +... +String coordinateFormat = messages.getMainMessage("coordinateFormat"); +FormatStrings formatStrings = Datatypes.getFormatStrings(userSessionSource.getLocale()); +NumberFormat format = new DecimalFormat(coordinateFormat, formatStrings.getFormatSymbols()); + +String formattedValue = format.format(value); + + +
+
+
+ Мета-аннотации + Мета-аннотации сущностей - набор пар ключ/значение, содержащих дополнительную информацию о сущностей. + Обращение к мета-аннотациям производится с помощью метода мета-класса getAnnotations(). + Источниками мета-аннотаций сущности являются: + + Аннотации @OnDelete, @OnDeleteInverse, @Extends. При этом в мета-аннотациях создаются служебные объекты связей между сущностями. + + + Аннотации @SystemLevel, @EnableRestore, @TrackEditScreenHistory. При этом создаются мета-аннотации с ключами, соответствующими полному имени класса Java аннотации. + + + Опционально: в прикладном проекте могут быть определены свои аннотации для сущностей, и в переопределенном методе MetadataImpl.initMetaAnnotations() отображены в соответствующие мета-аннотации. + + + Опционально: в файлах + metadata.xml + также могут быть определены мета-аннотации сущностей. Если мета-аннотация в XML имеет то же имя, что и мета-аннотация, созданная по Java аннотации класса сущности, первая переопределит значение второй. + Пример определения мета-аннотаций в + metadata.xml + :<annotations> + <entity class="com.haulmont.cuba.security.entity.User"> + <annotation name="com.haulmont.cuba.core.entity.annotation.TrackEditScreenHistory" value="false"/> + <annotation name="com.haulmont.cuba.core.entity.annotation.EnableRestore" value="true"/> + </entity> +</annotations> + + +
+
+
+ Представления +
+ Общие сведения + При извлечении сущностей из базы данных обычно встает вопрос - как обеспечить загрузку связанных сущностей на нужную глубину? + Например, для браузера Заказов нужно отобразить дату и сумму заказа совместно с названием Покупателя, т.е. загрузить связанный экземпляр Покупателя. А для экрана редактирования Заказа необходимо загрузить еще и коллекцию Пунктов заказа, причем каждый Пункт заказа должен содержать связанный экземпляр Товара для отображения его наименования. + Загрузка по требованию в большинстве случаев не может помочь, так как обработка данных как правило происходит не в транзакции, в которой загружаются сущности, а, например, на клиентском уровне в пользовательском интерфейсе. В то же время задание энергичной загрузки в аннотациях сущностей недопустимо, так как приводит к постоянному извлечению всего графа связанных сущностей, который может быть очень большим. + Другой похожей проблемой является ограничение набора локальных атрибутов сущностей загружаемого графа: например, некоторая сущность имеет 50 атрибутов, в том числе BLOB, а в экране отображается только 10 атрибутов. Зачем загружать из БД, затем сериализовать и передавать клиенту 40 атрибутов, которые ему в данный момент не нужны? + Механизм представлений (views) решает эти проблемы, обеспечивая извлечение из базы данных и передачу клиенту графов сущностей, ограниченных в глубину и по атрибутам. Представление является описателем графа объектов, который требуется в некотором экране UI или другом процессе обработки данных. + Обработка представлений производится следующим образом: + + все связи в модели данных объявляются с признаком загрузки по требованию (fetch = FetchType.LAZY, см. ) + + + в процессе загрузки данных через DataService клиентский код помимо JPQL запроса указывает нужное представление + + + на основе представления формируется так называемый Fetch Plan - особенность лежащего в основе слоя ORM фреймворка Apache OpenJPA. Fetch Plan влияет на формирование SQL запроса к базе данных: как на список возвращаемых полей, так и на соединения с другими таблицами, содержащими связанные сущности. + + + ссылки, не включенные в Fetch Plan (иногда это полезно для упрощения основного SQL запроса), загружаются отдельными SQL запросами, для чего механизм обработки представлений просто обращается к соответствующим методам чтения атрибутов + + + в результате на момент завершения транзакции, загружающей данные, в памяти Middleware содержится граф объектов, заданный JPQL запросом и представлением. + + +
+ Классы представления + + + + + +
+ Представление определяется экземпляром класса View, в котором: + + entityClass - класс сущности, для которого определено представление. Другими словами, "корень" дерева загружаемых сущностей. + + + name - имя представления. Должно быть либо null, либо уникальным в пределах данной сущности. + + + properties - коллекция экземпляров класса ViewProperty, соответствующих загружаемым атрибутам сущности. + + + includeSystemProperties - признак включения системных атрибутов (входящих в состав базовых интерфейсов персистентных сущностей BaseEntity и Updatable). Системные атрибуты не перечисляются в properties явно, а учитываются механизмом обработки представлений в зависимости от того, какие интерфейсы реализует данная сущность. + + + Класс ViewProperty имеет следующие свойства: + + name - имя атрибута сущности + + + view - для ссылочных атрибутов задает представление, с которым необходимо загружать связанную сущность + + + lazy - для ссылочных атрибутов признак того, что данный атрибут нужно не включать в Fetch Plan, а загружать отдельным SQL запросом, инициированным обращением к атрибуту. Следует иметь в виду, что атрибут в любом случае будет загружен на момент окончания транзакции, данный признак влияет только на способ загрузки. + + + + Независимо от набора атрибутов, определенного в представлении, всегда загружаются следующие атрибуты: + + id - идентификатор сущности + + + version - для оптимистично блокируемых сущностей, реализующих Versioned + + + deleteTs, deletedBy - для сущностей, реализующих + SoftDelete + + + + +
+
+ Создание представлений + Представление может быть создано двумя путями: + + программно - созданием экземпляра View, например:View view = new View(Order.class) + .addProperty("date") + .addProperty("amount") + .addProperty("customer", new View(Customer.class) + .addProperty("name") + ); + Как правило, таким способом создаются представления, используемые только в каком-то одном месте бизнес-логики. + + Механизм обработки представлений кэширует создаваемые для именованных представлений объекты Fetch Plan. Это означает, что если Вы программно создадите два представления для одной сущности с одинаковыми именами, но с разным набором атрибутов, то выборка со вторым представлением окажется неверной. + Поэтому никогда не задавайте имен для "одноразовых" представлений, создаваемых программно. + + + + декларативно - путем создания описателя на XML и его развертывания в репозитории представлений ViewRepository. При развертывании на основе XML-описателя создаются и кэшируются экземпляры View. В дальнейшем в любом месте кода приложения требуемое представление можно получить вызовом репозитория с указанием класса сущности и имени представления. + + + Рассмотрим подробнее декларативный способ создания и работы с представлениями. + Ссылка на ViewRepository может быть получена через интерфейс инфраструктуры + Metadata + . Для получения экземпляра View, содержащегося в репозитории, используются методы getView(). Для развертывания XML-описателей представлений в репозитории используются методы deployViews(). + В репозитории для каждой сущности по умолчанию доступны два представления с именами _local и _minimal: + + _local включает в себя все локальные атрибуты сущности + + + _minimal включает в себя атрибуты, входящие в имя экземпляра сущности, и задаваемые аннотацией + @NamePattern + . Если аннотация @NamePattern для сущности не указана, данное представление не включает никаких атрибутов. + + + Подробная структура XML-описателей изложена в + Пример описателя представления для сущности Заказ, которое должно обеспечить загрузку всех локальных атрибутов, ассоциированного Покупателя и коллекции Пунктов заказа:<view class="com.sample.sales.entity.Order" + name="orderWithCustomer" + extends="_local"> + <property name="customer" view="_minimal"/> + <property name="items" view="itemsInOrder"/> +</view> + Рекомендуемый способ группировки и развертывания описателей представлений: + + В модуле core в корне src создать файл {имя_проекта}-views.xml и поместить в него все описатели представлений, которые должны быть доступны глобально, т.е. на всех уровнях приложения. + + + Зарегистрировать данный файл в свойстве + cuba.viewsConfig + блока Middleware, т.е. в файле {имя_проекта}-app.properties модуля core. Это обеспечит автоматическое развертывание представлений на старте приложения в репозитории Middleware (см. метод MetadataImpl.initViews()). Развернутые представления будут доступны и для Middleware, и для клиентских блоков, которые получат соответствующие экземпляры View при подключении к среднему слою. + + + Если существуют представления, которые необходимы только какому-то одному клиентскому блоку приложения, то можно определить их в аналогичном файле этого блока, например {имя_проекта}-web-views.xml, и зарегистрировать в свойстве + cuba.viewsConfig + этого блока, т.е. в данном случае в файле {имя_проекта}-web-app.properties. Тогда клиентский блок сначала загрузит все имеющиеся представления с Middleware, а затем развернет свои собственные (см. MetadataClientImpl.initViews()). + Возможна также ситуация, когда некоторому клиентскому блоку не нужны глобальные представления, зарегистрированные на среднем слое, или нужны редко. В этом случае имеет смысл объявить в этом клиентском блоке свойство + cuba.lazyLoadServerViews + , тогда глобальные представления будут загружаться на клиентский уровень не все сразу, а по необходимости. + + + Если на момент развертывания некоторого представления в репозитории уже есть представление для этого же класса сущности и с таким же именем, то новое будет проигнорировано. Для того, чтобы представление заменило имеющееся в репозитории и гарантированно было развернуто, в XML-описателе должен быть явно указан атрибут overwrite = "true". В частности, развертывание представлений клиентского уровня производится после загрузки представлений со среднего слоя, поэтому некоторое клиентское представление может оказаться "замаскированным" одноименным представлением Middleware. + + Рекомендуется давать представлениям "описательные" имена. Например, не "browse", а "customerBrowse". Это упрощает поиск XML-описателей представлений по имени в процессе разработки приложения. + +
+
+
+ Интерфейсы инфраструктуры + Интерфейсы инфраструктуры обеспечивают доступ к часто используемой функциональности платформы. Большинство из этих интерфейсов расположены в модуле global и могут быть использованы как на среднем слое, так и в блоках клиентского уровня, но некоторые доступны только коду среднего слоя. + Интерфейсы инфраструктуры реализуются бинами Spring Framework, поэтому они могут быть инжектированы в любые другие управляемые компоненты (Managed Beans, сервисы среднего слоя, контроллеры экранов универсального пользовательского интерфейса. + Кроме того, как и любые другие бины, интерфейсы инфраструктуры могут быть получены с помощью статических методов класса AppBeans, и использоваться в неуправляемых компонентах (POJO, вспомогательных классах и пр.). +
+ Configuration + Позволяет получать ссылки на конфигурационные интерфейсы. + Примеры:@Inject +protected Configuration configuration; +... +String tempDir = configuration.getConfig(GlobalConfig.class).getTempDir();protected GlobalConfig globalConfig; + +@Inject +public void setConfiguration(Configuration configuration) { + this.globalConfig = configuration.getConfig(GlobalConfig.class); +}String tempDir = AppBeans.get(Configuration.class).getConfig(GlobalConfig.class).getTempDir(); +
+
+ Messages + Интерфейс Messages обеспечивает получение локализованных строк сообщений. + Рассмотрим методы интерфейса подробнее. + + + getMessage() - возвращает локализованное сообщение по ключу, имени пакета сообщений и требуемой локали. Существует несколько модификаций данного метода в зависимости от набора параметров. Если локаль не указана в параметре метода, используется локаль текущего пользователя. + Примеры:@Inject +protected Messages messages; +... +String message1 = messages.getMessage(getClass(), "someMessage"); +String message2 = messages.getMessage("com.abc.sales.web.customer", "someMessage"); +String message3 = messages.getMessage(RoleType.STANDARD); + + + formatMessage() - находит локализованное сообщение по ключу, имени пакета сообщений и требуемой локали, и использует его для форматирования переданных параметров. Формат задается по правилам метода String.format(). Существует несколько модификаций данного метода в зависимости от набора параметров. Если локаль не указана в параметре метода, используется локаль текущего пользователя. + Пример:String formattedValue = messages.formatMessage(getClass(), "someFormat", someValue); + + + getMainMessage() - возвращает локализованное сообщение из главного пакета данного блока приложения. + Пример:protected Messages messages = AppBeans.get(Messages.class); +... +messages.getMainMessage("actions.Ok"); + + + getMainMessagePack() - возвращает имя главного пакета сообщений данного блока приложения. + Пример:String formattedValue = messages.formatMessage(messages.getMainMessagePack(), "someFormat", someValue); + + + getTools() - возвращает экземпляр интерфейса MessageTools (см. ниже). + + +
+ MessageTools + ManagedBean, содержащий вспомогательные методы работы с локализованными сообщениями. Интерфейс MessageTools можно получить либо методом Messages.getTools(), либо как любой другой бин - инжекцией или через класс AppBeans. + Методы MessageTools: + + loadString() - возвращает локализованное сообщение, заданное ссылкой вида msg://{messagePack}/{key} + Составные части ссылки: + + msg:// - обязательный префикс + + + {messagePack} - необязательное имя пакета сообщения. Если не указано, предполагается, что имя пакета передается в loadString() отдельным параметром + + + {key} - ключ сообщения в пакете + + + Примеры ссылок на сообщения:msg://someMessage +msg://com.abc.sales.web.customer/someMessage + + + getEntityCaption() - возвращает локализованное название сущности + + + getPropertyCaption() - возвращает локализованное название атрибута сущности + + + hasPropertyCaption() - определяет, задано ли для атрибута сущности локализованное название + + + getLocValue() - возвращает локализованное значение атрибута сущности, основываясь на определении аннотации + @LocalizedValue + + + + getMessageRef() - формирует для мета-свойства ссылку на сообщение, по которой можно получить локализованное название атрибута сущности + + + Для расширения набора вспомогательных методов в конкретном приложении бин MessageTools можно переопределить. Примеры работы с расширенным интерфейсом:MyMessageTools tools = messages.getTools(); +tools.foo();((MyMessageTools) messages.getTools()).foo(); +
+
+
+ Metadata + Интерфейс Metadata обеспечивает доступ к сессии метаданных и репозиторию представлений. + Методы интерфейса: + + getSession() - возвращает экземпляр сессии метаданных + + + getViewRepository() - возвращает экземпляр репозитория представлений + + + getExtendedEntities() - возвращает экземпляр ExtendedEntities, предназначенный для работы с расширенными сущностями. Подробнее см. + + + create() - создать экземпляр сущности, учитывая возможность расширения. Подробнее см. + + + getTools() - возвращает экземпляр интерфейса MetadataTools (см. ниже). + + +
+ MetadataTools + ManagedBean, содержащий вспомогательные методы работы с метаданными. Интерфейс MetadataTools можно получить либо методом Metadata.getTools(), либо как любой другой бин - инжекцией или через класс AppBeans. + Методы MetadataTools: + + getAllPersistentMetaClasses() - возвращает коллекцию мета-классов персистентных сущностей + + + getAllEmbeddableMetaClasses() - возвращает коллекцию мета-классов встраиваемых сущностей + + + getAllEnums() - возвращает коллекцию классов перечислений, используемых в качестве типов атрибутов сущностей + + + format() - форматирует переданное значение в соответствии с типом данных заданного мета-свойства + + + isSystem() - определяет, является ли переданное мета-свойство системным, т.е. заданным в одном из базовых интерфейсов сущностей + + + isPersistent() - определяет, является ли переданное мета-свойство персистентным, т.е. хранимым в БД + + + isTransient() - определяет, является ли переданное мета-свойство или произвольный атрибут неперсистентным + + + isEmbedded() - определяет, является ли переданное мета-свойство встроенным объектом + + + isAnnotationPresent() - определяет наличие указанной аннотации на классе или его предках + + + getNamePatternProperties() - возвращает коллекцию мета-свойств атрибутов, входящих в имя экземпляра, возвращаемого методом Instance.getInstanceName(). См. + @NamePattern + . + + + Для расширения набора вспомогательных методов в конкретном приложении бин MetadataTools можно переопределить. Примеры работы с расширенным интерфейсом:MyMetadataTools tools = metadata.getTools(); +tools.foo();((MyMetadataTools) metadata.getTools()).foo(); +
+
+
+ Persistence + Интерфейс уровня Middleware, являющийся точкой входа в функциональность хранения данных в БД. + Интерфейс Persistence доступен только в модуле core проекта приложения. + + Методы интерфейса: + + createTransaction(), getTransaction() - получить интерфейс управления транзакциями + + + isInTransaction() - определяет, существует ли в данный момент активная транзакция + + + getEntityManager() - возвращает экземпляр + EntityManager + для текущей транзакции + + + isSoftDeletion() - позволяет определить, активен ли режим мягкого удаления + + + setSoftDeletion() - устанавливает или отключает режим мягкого удаления. Влияет на аналогичный признак всех создаваемых экземпляров EntityManager. По умолчанию мягкое удаление включено. + + + getDbDialect() - возвращает диалект используемой в данный момент базы данных. Интерфейс DbDialect определяет тип БД и некоторые специфические для данной БД параметры. + В прикладном коде данный метод можно использовать для определения типа используемой БД, например:@Inject +protected Persistence persistence; +... +if (persistence.getDbDialect() instanceof PostgresDbDialect) +... + + + getDataSource() - получить javax.sql.DataSource для используемой в данный момент базы данных. + + Для всех объектов javax.sql.Connection, получаемых методом getDataSource().getConnection(), необходимо после использования соединения вызвать метод close() в секции finally. В противном случае соединение не вернется в пул, через какое-то время пул переполнится, и приложение не сможет выполнять запросы к базе данных. + + + + getTools() - возвращает экземпляр интерфейса PersistenceTools (см. ниже). + + +
+ PersistenceTools + ManagedBean, содержащий вспомогательные методы работы с хранилищем данных. Интерфейс PersistenceTools можно получить либо методом Persistence.getTools(), либо как любой другой бин - инжекцией или через класс AppBeans. + Методы PersistenceTools: + + getDirtyFields() - возвращает коллекцию имен атрибутов сущности, измененных со времени последней загрузки экземпляра из БД. Для новых экземпляров возвращает пустую коллекцию. + + + isLoaded() - определяет, загружен ли из БД указанный атрибут экземпляра. Атрибут может быть не загружен, если он не указан в примененном при загрузке представлении. + Данный метод работает только для экземпляров в состоянии Managed. + + + getReferenceId() - возвращает идентификатор связанной сущности без загрузки ее из БД. + Предположим, в персистентный контекст загружен экземпляр Order, и нужно получить значение идентификатора экземпляра Customer, связанного с данным Заказом. Стандартное решение order.getCustomer().getId() приведет к выполнению SQL запроса к БД для загрузки экземпляра Customer, что в данном случае избыточно, так как значение идентификатора Покупателя физически находится также и в таблице Заказов. Выполнение же persistence.getTools().getReferenceId(order, "customer")не вызывет никаких дополнительных запросов к базе данных. + Данный метод работает только для экземпляров в состоянии Managed. + + + reloadEntity() - перезагрузить экземпляр сущности с указанным представлением. Данный метод должен вызываться внутри активной транзакции. + + Для расширения набора вспомогательных методов в конкретном приложении бин PersistenceTools можно переопределить. Примеры работы с расширенным интерфейсом:MyPersistenceTools tools = persistence.getTools(); +tools.foo();((MyPersistenceTools) persistence.getTools()).foo(); +
+
+ PersistenceHelper + Вспомогательный класс для получения информации о персистентных сущностях. В отличие от бинов Persistence и PersistenceTools доступен на всех уровнях приложения. + Методы PersistenceHelper: + + isNew() - определяет, является ли переданный экземпляр только что созданным, т.е. находящимся в состоянии New. Возвращает true также если экземпляр не является персистентной сущностью. + + + isDetached() - определяет, находится ли переданный экземпляр в состоянии Detached. Возвращает true также если экземпляр не является персистентной сущностью. + + + isSoftDeleted() - определяет, поддерживает ли переданный класс сущности мягкое удаление + + + getEntityName() - возвращает имя сущности, заданное в аннотации @Entity + + + getTableName() - возвращает имя таблицы БД, хранящей экземпляры сущности, заданное в аннотации @Table + + +
+
+
+ Resources + Обеспечивает загрузку ресурсов по следующим правилам: + + если указанное местонахождение представляет собой URL, ресурс загружается из этого URL; + + + если указанное местонахождение начинается с префикса classpath:, ресурс загружается из classpath; + + + если не URL и не начинается с classpath:, то: + + в каталоге конфигурации приложения ищется файл, используя указанное местонахождение как относительный путь. Если файл найден, ресурс загружается из него; + + + если ресурс не найден на предыдущих этапах, он загружается из classpath. + + + + + На практике явное указание URL или префикса classpath: используется редко, т.е. обычно ресурсы загружаются либо из конфигурационного каталога, либо из classpath. Ресурс в конфигурационном каталоге замещает одноименный ресурс в classpath. + Методы Resources: + + getResourceAsStream() - возвращает InputStream для указанного ресурса, либо null, если ресурс не найден. Поток должен быть закрыт после использования, например:@Inject +protected Resources resources; +... +InputStream stream = null; +try { + stream = resources.getResourceAsStream(resourceLocation); + ... +} finally { + IOUtils.closeQuietly(stream); +} + Возможно использование "try with resources":try (InputStream stream = resources.getResourceAsStream(resourceLocation)) { + ... +} + + + getResourceAsString() - возвращает указанный ресурс в виде строки, либо null, если ресурс не найден + + +
+
+ Scripting + Интерфейс Scripting позволяет динамически (т.е. во время работы приложения) компилировать и загружать классы Java и Groovy, а также выполнять скрипты и выражения на Groovy. + Методы Scripting: + + evaluateGroovy() - выполняет выражение на Groovy и возвращает его результат. + Свойство приложения + cuba.groovyEvaluatorImport + позволяет определить общий набор импортируемых классов, подставляемых в каждое выполняемое выражение. По умолчанию все стандартные блоки приложения импортируют класс + PersistenceHelper + . + Скомпилированные выражения кэшируются, что значительно ускоряет повторное выполнение. + Пример:@Inject +protected Scripting scripting; +... +Integer intResult = scripting.evaluateGroovy("2 + 2", new Binding()); + +Binding binding = new Binding(); +binding.setVariable("instance", new User()); +Boolean boolResult = scripting.evaluateGroovy("return PersistenceHelper.isNew(instance)", binding); + + + runGroovyScript() - выполняет скрипт Groovy и возвращает его результат. + Скрипт должен быть расположен либо в конфигурационном каталоге приложения, либо в classpath (текущая реализация Scripting поддерживает ресурсы classpath только внутри JAR-файлов). Скрипт в конфигурационном каталоге замещает одноименный скрипт в classpath. + Путь к скрипту указывается с разделителями /, в начале пути символ / не требуется. + Пример:@Inject +protected Scripting scripting; +... +Binding binding = new Binding(); +binding.setVariable("itemId", itemId); +BigDecimal amount = scripting.runGroovyScript("com/abc/sales/CalculatePrice.groovy", binding); + + + loadClass() - загружает Java или Groovy класс, используя следующую последовательность действий: + + Если класс уже загружен, возвращает его. + + + Ищет исходный текст Groovy (файл *.groovy) в каталоге конфигурации. Если найден, компилирует его, загружает и возвращает класс. + + + Ищет исходный текст Java (файл *.java) в каталоге конфигурации. Если найден, компилирует его, загружает и возвращает класс. + + + Ищет скомпилированный класс в classpath, если найден - загружает и возвращает его. + + + Если ничего не найдено, возвращает null. + + + Файлы исходных текстов Java и Groovy в каталоге конфигурации можно изменять во время работы приложения. При следующем вызове loadClass() соответствующий класс будет перекомпилирован и возвращен новый, однако существуют следующие ограничения: + + нельзя изменять тип исходного текста с Groovy на Java + + + если существовал исходный текст Groovy, и был однажды скомпилирован, то удаление файла исходного текста не приведет к загрузке другого класса из classpath - будет по прежнему возвращаться класс, скомпилированный из удаленного исходника. + + + Пример:@Inject +protected Scripting scripting; +... +Class calculatorClass = scripting.loadClass("com.abc.sales.PriceCalculator"); + + + getClassLoader() - возвращает ClassLoader, способный работать по правилам, описанным выше для метода loadClass(). + + + Кэш скомпилированных классов можно очистить во время выполнения с помощью JMX-бинов CachingMBean и CachingFacadeMBean. + См. также +
+
+ Security + Обеспечивает авторизацию - проверку прав пользователя на различные объекты системы. Большинство методов просто получают текущую пользовательскую сессию через UserSessionSource и делегируют ей выполнение авторизации. Подробнее см. +
+
+ TimeSource + Обеспечивает получение текущего времени. Применение new Date() и т.п. в прикладном коде не рекомендуется. + Примеры:@Inject +protected TimeSource timeSource; +... +Date date = timeSource.currentTimestamp();long startTime = AppBeans.get(TimeSource.class).currentTimeMillis(); +
+
+ UserSessionSource + Обеспечивает получение объекта сессии текущего пользователя. Подробнее см. +
+
+ UuidSource + Обеспечивает получение значений UUID, в том числе для идентификаторов сущностей. Применение UUID.randomUUID() в прикладном коде не рекомендуется. +
+
+
+ AppContext + AppContext - системный класс, в статических полях которого хранятся ссылки на некоторые общие для любого блока приложения компоненты: + + ApplicationContext фреймворка Spring + + + Набор свойств приложения, загруженных из файлов app.properties + + + ThreadLocal переменная, хранящая экземпляры SecurityContext + + + Коллекция слушателей жизненного цикла приложения ( AppContext.Listener) + + + AppContext инициализируется на запуске приложения классами-загрузчиками, специфичными для типа блока приложения: + + загрузчик Middleware - AppContextLoader + + + загрузчик Web Client - WebAppContextLoader + + + загрузчик Web Portal - PortalAppContextLoader + + + загрузчик Desktop Client - DesktopAppContextLoader + + + AppContext может быть использован в прикладном коде для решения следующих задач: + + Регистрации слушателей, срабатывающих после полной инициализации и перед закрытием приложения, например:AppContext.addListener(new AppContext.Listener() { + @Override + public void applicationStarted() { + System.out.println("Application is ready"); + } + + @Override + public void applicationStopped() { + System.out.println("Application is closing"); + } +}); + + + Проверки того, что данный блок приложения полностью инициализирован и готов к выполнению. Такая проверка обычно необходима в методах бинов, автоматически запускаемых планировщиком Sping Framework. + Пример:if (!AppContext.isStarted()) + return; + + + Получения значений свойств приложения, хранимых в файлах app.properties, если они недоступны через конфигурационные интерфейсы. + + + Передачи SecurityContext в новые потоки выполнения, см. . + + + + Для получения ссылок на Spring-бины используйте инжекцию или статические методы класса AppBeans. + Использование AppContext.getApplicationContext().getBean() не рекомендуется. + +
+
+ Свойства приложения +
+ Основные понятия + Свойства приложения − именованные данные различных типов, определяющие всевозможные аспекты конфигурации и функционирования приложения. + По назначению свойства приложения можно классифицировать следующим образом: + + + Конфигурационные параметры - задают наборы конфигурационных файлов и некоторые параметры пользовательского интерфейса, т.е. определяют функциональность приложения. + Например: cuba.springContextConfig, cuba.web.useLightHeader. + + + Параметры развертывания - различные URL для соединения блоков приложения, тип используемой БД, настройки подсистемы безопасности и т.д. + Например: cuba.connectionUrl, cuba.dbmsType, + cuba.userSessionExpirationTimeoutSec + . + + + Параметры времени выполнения - активность аудита, параметры отсылки email и т.д. + Например: cuba.security.EntityLog.enabled, cuba.email.smtpHost. + + + Как правило, некоторое свойство принадлежит только одному или нескольким блокам приложения. Например, + cuba.persistenceConfig + имеет смысл только для Middleware, cuba.web.useLightHeader − только для Web Client, а + cuba.springContextConfig + − для всех блоков. + Принадлежность к блоку означает, что если нужно задать значение некоторому свойству, это необходимо сделать во всех блоках, которым данное свойство принадлежит (и которые используются в приложении). + Принадлежность можно выяснить следующими способами: + + Из документации: см. + + + Проследив использование свойства в коде приложения + + + Если к свойству есть доступ через конфигурационный интерфейс, то по принадлежности интерфейса модулю проекта. + + +
+
+ Доступ к свойствам + Основной способ доступа к свойствам приложения из прикладного кода − использование механизма конфигурационных интерфейсов. Кроме того, все параметры конфигурации и развертывания доступны через методы класса + AppContext + . + Некоторые блоки приложения определяют JMX-интерфейсы для доступа к свойствам приложения. В частности, на уровне Middleware имеется JMX-интерфейс + ConfigStorageMBean + , а на уровне Web Client + ConfigurationMBean + . Методы этих интерфейсов работают по отдельности с параметрами конфигурации и развертывания (*AppProperties) и с параметрами времени выполнения (*DbProperties). Это обусловлено различием механизмов хранения этих категорий свойств. +
+
+ Хранение свойств в файлах + Свойства, определяющие конфигурацию и параметры развертывания, задаются в специальных файлах свойств, имеющих имя вида *-app.properties. Каждый блок приложения имеет набор таких файлов, включающий в себя файлы из базовых проектов платформы и файл текущего приложения. Набор файлов свойств определяется следующим образом: + + + Для блоков, являющихся веб-приложениями (Middleware, Web Client, Web Portal) набор файлов свойств задается в web.xml в параметре appPropertiesConfig. + + + Для блока Desktop Client основной способ задания набора файлов свойств − переопределение в приложении метода getDefaultAppPropertiesConfig() в классе-наследнике com.haulmont.cuba.desktop.App. + + + Например, набор файлов свойств блока Middleware проекта sales задается в файле web/WEB-INF/web.xml модуля core, и выглядит следующим образом: + classpath:cuba-app.properties +classpath:sales-app.properties +file:${catalina.home}/conf/app-core/local.app.properties + Здесь префикс classpath: означает, что данный файл нужно искать в Java classpath, префикс file: − в файловой системе. Возможно использование системных свойств Java, в данном случае это catalina.home − путь к корню Tomcat. + Порядок перечисления файлов важен, так как значения, указанные в каждом последующем файле заменяют значения одноименных свойств, заданные в предыдущих файлах. Этим достигается переопределение свойств платформы в конкретном приложении. + Последний файл в приведенном наборе − local.app.properties. Он может использоваться для переопределения свойств приложения при развертывании. Если этого файла нет, он игнорируется. Если же во время инсталляции системы требуется переопределение некоторых параметров (как правило различных URL), достаточно создать этот файл и поместить в него переопределяемые свойства. При последующих обновлениях системы такой файл с локальными настройками легко сохранить. + Аналогом local.app.properties для Desktop Client служат аргументы командной строки запуска JVM. Загрузчик свойств данного блока воспринимает все аргументы, содержащие знак "=", как пары ключ-значение, и заменяет ими соответствующие свойства приложения, заданные в файлах app.properties. + + Правила задания информации в файлах *.properties: + + + Кодировка файла - UTF-8 + + + Ключ может состоять из латинских букв, цифр, точек и знаков подчеркивания + + + Значение пишется после знака равно (=) + + + Значение не нужно брать в кавычки " или ' + + + Файловые пути записываются либо в UNIX виде (/opt/haulmont/), либо в Windows виде (c:\\haulmont\\) + + + Возможно использование кодов \n \t \r. Символ \ является зарезервированным, для вставки в значение экранируется сам собой (\\). +Подробнее см.: http://docs.oracle.com/javase/tutorial/java/data/characters.html + + + Для ввода значения в нескольких строках файла используйте символ \ в конце строки, для того чтобы данное значение продолжалось на следующей строке. + + + +
+
+ Хранение свойств в базе данных + Параметры времени выполнения хранятся в таблице SYS_CONFIG базы данных. + Такие свойства имеют следующие особенности: + + так как значение свойства хранится в базе данных, оно задается в одном месте, независимо от того, в каких блоках приложения оно используется + + + значение может быть изменено и сохранено во время работы приложения, как через конфигурационный интерфейс, содержащий это свойство, так и через ConfigStorageMBean. + + + Хранящиеся в БД свойства кэшируются на уровне Middleware. Очистить кэш можно с помощью JMX-интерфейсов + ConfigStorageMBean + методом clearCache() или + CachingFacadeMBean + методом clearConfigStorageCache(). + Следует иметь в виду, что на клиентском уровне чтение свойства, хранящегося в БД, приводит к запросу к Middleware, что менее эффективно, чем чтение локального свойства из app.properties. Для уменьшения количества таких запросов клиент кэширует все свойства, хранящиеся в БД, на время жизни экземпляра реализации конфигурационного интерфейса. Поэтому если например в некотором экране UI необходимо несколько раз обратиться к свойствам одного конфигурационного интерфейса, лучше получить ссылку на него при инициализации экрана, и сохранить в поле для последующих обращений к одному и тому же экземпляру. +
+
+ Конфигурационные интерфейсы + Данный механизм позволяет работать со свойствами приложения через методы Java-интерфейсов, что дает следующие преимущества: + + типизированность - прикладной код работает с нужными типами (String, Boolean, Integer и пр.), а не только со строками + + + в прикладном коде вместо строковых идентификаторов свойств используются методы интерфейсов, имена которых подсказываются средой разработки + + + Пример получения значения таймаута транзакции в блоке Middleware:@Inject +protected Configuration configuration; +... +ServerConfig serverConfig = configuration.getConfig(ServerConfig.class); +int timeout = serverConfig.getDefaultQueryTimeoutSec(); + Или при невозможности инжекции Configuration:int timeout = AppBeans.get(Configuration.class) + .getConfig(ServerConfig.class) + .getDefaultQueryTimeoutSec(); +
+ Использование + Для создания конфигурационного интерфейса необходимо: + + + Создать интерфейс, унаследованный от com.haulmont.cuba.core.config.Config (не путать с классом сущности com.haulmont.cuba.core.entity.Config) + + + Добавить интерфейсу аннотацию @Source для указания источника (способа хранения) параметров: + + SourceType.SYSTEM - значение свойства будет взято из системных свойств данной JVM, т.е. методом System.getProperty() + + + SourceType.APP - значение свойства будет взято из файлов app.properties + + + SourceType.DATABASE - значение свойства будет взято из таблицы SYS_CONFIG + + + + + Создать методы доступа к свойству (getter / settter). Если значение свойства не предполагается изменять во время выполнения, метод доступа на запись не нужен. Возможный тип свойства рассмотрен ниже. + + + Добавить методу доступа на чтение аннотацию @Property, определяющую имя свойства. + + + Опционально аннотацию @Source можно задать для отдельного свойства в интерфейсе, если его источник отличается от заданного для всего интерфейса. + + + Например:@Source(type = SourceType.DATABASE) +public interface SalesConfig extends Config { + + @Property("sales.companyName") + String getCompanyName(); +} + Создавать класс реализации конфигурационного интерфейса не нужно - при получении ссылки на интерфейс через Configuration будет автоматически создан необходимый прокси-объект. +
+
+ Типы свойств + Без дополнительных усилий поддерживаются следующие типы свойств: + + String + + + простые типы либо их объектные обертки (boolean, Boolean, int, Integer, etc.) + + + классы персистентных сущностей. При обращении к свойству типа сущности происходит загрузка из БД экземпляра, заданного значением свойства. + + + Для поддержки произвольного типа необходимо реализовать классы TypeStringify и TypeFactory для преобразования значения в строку и из нее, и указать эти классы для свойства с помощью аннотаций @Stringify и @Factory. + Рассмотрим этот процесс на примере типа UUID. + + + Создаем класс com.haulmont.cuba.core.config.type.UuidTypeFactory унаследованный от com.haulmont.cuba.core.config.type.TypeFactory и реализуем в нем метод:public Object build(String string) { + if (string == null) + return null; + return UUID.fromString(string); +} + + + TypeStringify создавать не нужно, т.к. по умолчанию будет использован метод toString() − в данном случае он нам подходит. + + + Аннотируем свойство в конфигурационном интерфейсе:@Factory(factory = UuidTypeFactory.class) +UUID getUuidProp(); +void setUuidProp(UUID value); + + +
+
+ Значения по умолчанию + Для свойств конфигурационных интерфейсов могут быть заданы значения по умолчанию. Эти значения будут возвращаться, если данный параметр не задан в месте хранения - в БД или в app.properties. + Значение по умолчанию может быть задано в виде строки с помощью аннотации @Default, либо в виде конкретного типа с помощью других аннотаций пакета com.haulmont.cuba.core.config.defaults:@Property("cuba.email.adminAddress") +@Default("address@company.com") +String getAdminAddress(); + +@Property("cuba.email.delayCallCount") +@Default("2") +int getDelayCallCount(); + +@Property("cuba.email.defaultSendingAttemptsCount") +@DefaultInt(10) +int getDefaultSendingAttemptsCount(); + Для сущностей значение по умолчанию задается строкой вида {entity_name}-{id}-{optional_view_name}, например:@Default("sec$User-98e5e66c-3ac9-11e2-94c1-3860770d7eaf-browse") +User getAdminUser(); + +@Default("sec$Role-a294aef0-3ac9-11e2-9433-3860770d7eaf") +Role getAdminRole(); +
+
+
+
+ Локализация сообщений + Приложение на основе платформы CUBA поддерживает локализацию сообщений, то есть вывод всех элементов пользовательского интерфейса на языке, выбранном пользователем. + Возможности выбора языка определяются комбинацией свойств приложения cuba.localeSelectVisible и cuba.availableLocales. + Для того, чтобы некоторое сообщение могло быть локализовано, т.е. представлено пользователю на нужном языке, его необходимо поместить в так называемый пакет сообщений. +
+ Пакеты сообщений + Пакет сообщений представляет собой набор файлов свойств с именами вида messages{_XX}.properties, расположенных в одном Java-пакете. Суффикс XX определяет язык, для которого в данном файле содержатся сообщения, и соответствует коду языка в Locale.getLanguage() (для поддержки остальных атрибутов локали необходимо установить в false свойство приложения + cuba.useLocaleLanguageOnly + ). Один из файлов пакета может быть без суффикса языка - это файл по умолчанию. Именем пакета сообщений считается имя Java-пакета, в котором расположены файлы пакета. + Рассмотрим пример:/com/abc/sales/gui/customer/messages.properties +/com/abc/sales/gui/customer/messages_fr.properties +/com/abc/sales/gui/customer/messages_ru.properties + Данный пакет состоит из 3-х файлов - один для русского языка, один для французского, и один по умолчанию. Имя пакета - com.abc.sales.gui.customer + Файлы сообщений содержат пары ключ-значение, где ключ - это идентификатор сообщения, на который ссылается код приложения, а значение - само сообщение на языке данного файла. Правила задания пар аналогичны правилам файлов свойствjava.util.Properties, со следующими особенностями: + + Кодировка файла - обязательно UTF-8 + + + Поддерживается включение других пакетов сообщений с помощью ключа @include, в том числе нескольких сразу - перечислением через запятую. При этом если некоторый ключ сообщения встречается и во включаемом пакете, и в текущем, будет использовано сообщение из текущего. Пример включения пакетов:@include=com.haulmont.cuba.web, com.abc.sales.web + +someMessage=Some Message +... + + + Получение сообщений из пакетов производится с помощью методов интерфейса + Messages + по следующим правилам: + + Сначала производится поиск в конфигурационном каталоге приложения + + Ищется файл messages_XX.properties в каталоге, задаваемом именем пакета сообщений, где XX - код требуемого языка + + + Если такого файла нет, в этом же каталоге ищется файл по умолчанию messages.properties + + + Если найден или файл нужного языка, или файл по умолчанию, он загружается вместе со всеми @include, и в нем ищется ключ сообщения + + + Если файл не найден, либо нужный ключ в нем отсутствует, производится смена каталога на родительский, и процедура поиска повторяется. И так до достижения корня конфигурационного каталога. + + + + + Если в конфигурационном каталоге сообщение не найдено, производится поиск в classpath по такому-же алгоритму. + + + На клиентском уровне, если сообщение не найдено на предыдущих шагах, отправляется запрос на Middleware, и сообщение ищется там аналогичным способом. + + + Если сообщение найдено, оно кэшируется и возвращается. Если не найдено - кэшируется факт отсутствия сообщения и возвращается ключ, который был передан для поиска. Таким образом, сложная процедура поиска выполняется только один раз, в дальнейшем результат загружается из локального для блока приложения кэша. + + + + Рекомендуется организовывать пакеты сообщений следующим образом: + + Если приложение не предполагает интернационализации, то можно не использовать пакеты и включать строки сообщений прямо в код приложения, либо пользоваться файлами по умолчанию messages.properties для отделения ресурсов от кода. + + + Если приложение интернациональное, логично файлы по умолчанию использовать для языка основной аудитории приложения, либо для английского языка. Именно сообщения из файлов по умолчанию будут показаны пользователю, если сообщений для нужного языка не найдено. + + + +
+
+ Главный пакет сообщений + Каждый стандартный блок приложения определяет для себя один главный пакет сообщений. Для блоков клиентского уровня этот пакет содержит названия пунктов главного меню и общих элементов UI (например названия кнопок OK и Cancel). Для всех блоков приложения, включая Midleware, главный пакет определяет форматы преобразований + Datatype + . + Для указания главного пакета соощений используется свойство приложения + cuba.mainMessagePack + . Значением свойства может быть либо один пакет, либо список пакетов, разделенный пробелами. Например:cuba.mainMessagePack=com.haulmont.cuba.web com.abc.sales.web + В данном случае сообщения, заданные во втором пакете списка будут перекрывать сообщений из первого пакета. Таким образом в проекте приложения можно переопределять сообщения, заданные в пакетах базовых проектов. +
+
+ Локализация названий сущностей и атрибутов + Для отображения в UI локализованных названий сущностей и их атрибутов необходимо создать специальные пакеты сообщений в тех же Java-пакетах, что и сами сущности. Формат файлов сообщений должен быть следующим: + + Ключ названия сущности - простое имя класса (без пакета) + + + Ключ названия атрибута - простое имя класса, затем через точку имя атрибута + + + Пример русской локализации сущнсти com.abc.sales.entity.Customer - файл /com/abc/sales/entity/messages_ru.properties:Customer=Покупатель +Customer.name=Имя +Customer.email=Email + +Order=Заказ +Order.customer=Покупатель +Order.date=Дата +Order.amount=Сумма + Такие пакеты сообщений как правило используются неявно для разработчика, например визуальными компонентами + Table + и + FieldGroup + . Кроме того, названия сущностей и атрибутов могут быть также получены следующими методами: + + программно - методами + MessageTools + getEntityCaption(), getPropertyCaption() + + + в XML дескрипторе экрана - указанием ссылки на сообщение по правилам MessageTools.loadString(): msg://{entity_package}/{key}, например caption="msg://com.abc.sales.entity/Customer.name" + + +
+
+ Локализация enum + Для локализации названий и значений перечислений необходимо в пакет сообщений, находящийся в Java-пакете класса перечисления добавить сообщения со следующими ключами: + + Ключ названия перечисления - простое имя класса (без пакета) + + + Ключ значения - простое имя класса, затем через точку имя значения + + + Например, для перечисления package com.abc.sales; + +public enum CustomerGrade { PREMIUM, HIGH, STANDARD } + файл русской локализации /com/abc/sales/messages_ru.properties должен содержать строки:CustomerGrade=Уровень покупателя +CustomerGrade.PREMIUM=Премиум +CustomerGrade.HIGH=Высокий +CustomerGrade.STANDARD=Стандартный + Локализованные значения перечислений автоматически используются различными визуальными компонентами, например + LookupField + . Для программного получения локализованного значения перечисления можно использовать метод getMessage() интерфейса + Messages + , просто передавая в него экземпляр enum. +
+
+
+ Аутентификация пользователей + В данном разделе рассмотрены некоторые аспекты управления доступом с точки зрения разработчика приложения. Для получения полной информации о возможностях и настройке доступа пользователей к данным обратитесь к руководству Платформа CUBA. Подсистема безопасности. +
+ UserSession + Основной элемент подсистемы контроля доступа в CUBA-приложении - пользовательская сессия. Это объект класса UserSession, который ассоциирован с аутентифицированным в данный момент в системе пользователем, и содержит информацию о правах доступа пользователя к данным. Объект текущей сессии может быть получен в любом блоке приложения через интерфейс инфраструктуры + UserSessionSource + . + Пользовательская сессия создается на Middleware при выполнении метода LoginService.login() после аутентификации пользователя по переданному имени и паролю. Объект UserSession затем кэшируется в данном блоке Middleware, и возвращается на клиентский уровень. Пр работе в кластере объект сессии реплицируется на соседние узлы кластера Middleware. Клиентский блок, получив объект сессии, также сохраняет его у себя, так или иначе ассоциируя с активным пользователем (например в HTTP сессии). Далее все вызовы Middleware для данного пользователя сопровождаются передачей идентификатора сессии (типа UUID), причем прикладному коду не нужно об этом заботиться - идентификатор сессии передается автоматически, независимо от сигнатуры вызываемых методов среднего слоя. Обработка вызовов клиентов на Middleware начинается с извлечения из кэша сессии по полученному идентификатору и установки ее в потоке выполнения. Объект сессии удаляется из кэша при вызове метода LoginService.logout(), либо при истечения времени бездействия, определяемого свойством приложения + cuba.userSessionExpirationTimeoutSec + . + Таким образом, идентификатор сессии, создаваемой при входе пользователя в систему, служит для аутентификации пользователя при каждом вызове среднего слоя. + Объект UserSession содержит также методы для авторизации текущего пользователя, т.е. проверки его прав на объекты системы: isScreenPermitted(), isEntityOpPermitted(), isEntityAttrPermitted(), isSpecificPermitted(). +
+
+ Вход в систему + Стандартный вариант входа пользователя: + + пользователь вводит свой логин и пароль + + + клиентский блок приложения хэширует пароль, вызывая метод getPlainHash() бина PasswordEncryption и вызывает на Middleware метод LoginService.login(), передавая ему логин пользователя и хэш пароля + + + LoginService делегирует выполнение бину LoginWorker, который загружает объект User по полученному логину, хэширует полученный хэш пароля повторно, используя в качестве соли идентификатор пользователя, и сравнивает полученный хэш с сохраненным в БД хэшем пароля. В случае несовпадения выбрасывается исключение LoginException. + + + После успешной аутентификации в созданный экземпляр + UserSession + загружаются все параметры доступа данного пользователя: список ролей, права, ограничения и атрибуты сессии. + + + Алгоритм хэширования паролей реализуется бином типа EncryptionModule и задается в свойстве приложения + cuba.passwordEncryptionModule + . По умолчанию - SHA-1. + Возможен вариант, когда пароль пользователя (точнее, хэш пароля) не хранится в базе данных, а проверяется внешними средствами, например путем интеграции с ActiveDirectory. В этом случае фактически аутентификацию выполняет клиентский блок, а Middleware "доверяет" клиенту, создавая сессию по одному только логину пользователя без пароля методом LoginService.loginTrusted(). Метод loginTrusted() требует выполнения следующих условия: + + клиентский блок должен передать так называемый доверенный пароль, задаваемый на Middleware и на клиентском блоке свойством приложения + cuba.trustedClientPassword + + + + IP адрес клиентского блока должен соответствовать маске, задаваемой свойством приложения + cuba.trustedClientPermittedIpMask + + + + Вход в систему требуется также для автоматических процессов, запускаемых по расписанию, а также при подключении к бинам Middleware через JMX-интерфейс. Строго говоря, такие действия считаются административными и не требуют аутентификации до тех пор, пока не выполняется каких-либо изменений сущностей в базе данных. При записи сущностей в БД требуется проставить логин пользователя, который выполнил изменения, поэтому для работы таких процессов должен быть указан пользователь, от лица которого выполняются изменения. + Дополнительным плюсом входа в систему для автоматического процесса и для JMX-вызова является то, что вывод в журнал сообщений от логгеров сопровождается указанием логина текущего пользователя, если пользовательская сессия установлена в потоке выполнения. Это упрощает поиск сообщений от конкретного процесса при разборе журнала. + Вход в систему для процессов внутри Middleware выполняется вызовом LoginWorker.loginSystem() с передачей логина пользователя (без пароля), от имени которого будет работать данный процесс. В результате создается объект + UserSession + , который будет закэширован в данном блоке Middleware и не будет реплицироваться в кластере. + Стандартная реализация входа / выхода для компонентов Middleware заключена в специальном базовом классе + ManagementBean + , поэтому свои JMX-бины рекомендуется наследовать от него. +
+
+ SecurityContext + Экземпляр класса SecurityContext хранит информацию о пользовательской сессии для текущего потока выполнения. Он создается и передается в метод AppContext.setSecurityContext() в следующие моменты: + + для блоков Web Client и Web Portal - в начале обработки каждого HTTP-запроса от пользовательского браузера + + + для блока Middleware - в начале обработки каждого запроса от клиентского уровня + + + для блока Desktop Client - один раз после входа пользователя, так как десктопное приложение является однопользовательским + + + По окончании выполнения запроса в первых двух случаях SecurityContext удаляется из потока выполнения. + При создании прикладным кодом нового потока выполнения в него необходимо передать текущий экземпляр SecurityContext, например:final SecurityContext securityContext = AppContext.getSecurityContext(); +executor.submit(new Runnable() { + public void run() { + AppContext.setSecurityContext(securityContext); + // business logic here + } +}); +
+
+
+
+ Компоненты среднего слоя + На следующем рисунке приведены основные компоненты среднего слоя CUBA-приложения. +
+ Компоненты среднего слоя + + + + + +
+ Services – управляемые контейнером компоненты, формирующие границу приложения и предоставляющие интерфейс клиентскому уровню приложения. Сервисы могут содержать бизнес-логику сами, либо делегировать выполнение Managed Beans. + Managed Beans – управляемые контейнером компоненты, содержащие бизнес-логику приложения. Вызываются сервисами, другими бинами или через опциональный JMX интерфейс. + + Persistence + − инфраструктурный интерфейс для доступа к функциональности хранения данных: управлению транзакциями и ORM. +
+ Сервисы + Сервисы образуют слой компонентов, определяющий множество операций Middleware, доступных клиентскому уровню приложения. Внутри сервисов инкапсулируется бизнес-логика и управление транзакциями. + Основные задачи сервисов: + + + Предоставляют удаленный (remote) интерфейс для вызова с клиентского уровня + + + Проверяют наличие активной пользовательской сессии, соответствующей идентификатору сессии, переданному с клиента + + + Записывают в журнал необработанные исключения среднего слоя + + + Кроме того, именно в слое сервисов рекомендуется выполнять авторизацию текущего пользователя, т.е. проверять его права на ту или иную функциональность. + Общие для всех сервисов задачи решаются следующим образом: + + Проверка наличия пользовательской сессии и логгирование исключений производится классом-интерцептором ServiceInterceptor, который перехватывает выполнение каждого метода сервиса с помощью Spring AOP + + + Удаленный интерфейс для доступа к сервису через Spring HTTP Invoker создается бином RemoteServicesBeanCreator, который конфигурируется в файле + remoting-spring.xml + модуля core. + + +
+ Диаграмма классов сервиса + + + + + +
+
+ Создание сервиса + Имена интерфейсов сервисов должны заканчиваться на Service, имена классов реализации на ServiceBean. + При создании сервиса необходимо выполнить следующее: + + + Создать интерфейс в модуле global (т.к. интерфейс сервиса должен быть доступен на всех уровнях) и задать в нем имя сервиса. Имя рекомендуется задавать в формате {имя_проекта}_{имя_интерфейса}. Например: + package com.sample.sales.core; + +import com.sample.sales.entity.Order; + +public interface OrderService { + String NAME = "sales_OrderService"; + + void calculateTotals(Order order); +} + + + Создать класс сервиса в модуле core и добавить ему аннотацию @org.springframework.stereotype.Service с именем, заданным в интерфейсе + package com.sample.sales.core; + +import com.sample.sales.entity.Order; +import org.springframework.stereotype.Service; + +@Service(OrderService.NAME) +public class OrderServiceBean implements OrderService { + @Override + public void calculateTotals(Order order) { + } +} + Класс сервиса, как и класс любого другого управляемого бина, должен находиться внутри дерева пакетов с корнем, заданным в элементе context:component-scan файла + spring.xml + . В нашем случае файл sales-spring.xml содержит элемент:<context:component-scan base-package="com.sample.sales"/>что означает, что поиск аннотированных бинов для данного блока приложения будет происходить начиная с пакета com.sample.sales. + + + + Сервисы предназначены только для вызова "снаружи" Middleware. Не рекомендуется вызывать методы сервисов из других компонентов среднего слоя. При обнаружении факта вызова сервиса из другого сервиса в журнал выводится сообщение об ошибке. + Если некоторую бизнес-логику требуется вызывать из разных сервисов либо других компонентов Middleware, ее необходимо выделить и инкапсулировать внутри соответствующего Managed Bean. + +
+
+ Использование сервиса + Для того чтобы вызывать сервис, в клиентском блоке приложения для него должен быть создан соответствующий прокси-объект. Делается это путем объявления имени и интерфейса сервиса в параметрах фабрики прокси-объектов. Для блока Web Client это бин класса WebRemoteProxyBeanCreator, для Web Portal - PortalRemoteProxyBeanCreator , для Desktop Client - RemoteProxyBeanCreator . + Фабрика прокси-объектов конфигурируется в файле + spring.xml + ссответствующего клиентского блока. + Например, чтобы в приложении sales вызвать с веб-клиента сервис sales_OrderService, необходимо добавить в файл sales-web-spring.xml модуля web следующее: + <bean id="sales_proxyCreator" class="com.haulmont.cuba.web.sys.remoting.WebRemoteProxyBeanCreator"> + <property name="clusterInvocationSupport" ref="cuba_clusterInvocationSupport"/> + <property name="remoteServices"> + <map> + <entry key="sales_OrderService" value="com.sample.sales.core.OrderService"/> + </map> + </property> +</bean> + Все импортируемые сервисы объявляются в одном свойстве remoteServices в элементах map/entry. + С точки зрения прикладного кода прокси-объект сервиса на клиентском уровне является обычным бином Spring и может быть получен либо инжекцией, либо с помощью класса AppBeans, например:@Inject +protected OrderService orderService; +... +orderService.calculateTotals(order); +
+
+
+ Управляемые бины + Управляемые бины (Managed Beans) − это программные компоненты, предназначенные для реализации бизнес-логики приложения. Термин "управляемые" в данном случае означает, что созданием экземпляров и установкой связей между такими компонентами управляет контейнер, который является основной частью фреймворка Sping. + + Managed Bean представляет собой singleton, то есть в некотором блоке приложения существует только один экземпляр данного класса. Поэтому, если бин содержит изменяемые данные в полях (другими словами, имеет состояние), то обращение к таким данным необходимо синхронизировать. + +
+ Создание бина + Для создания управляемого бина достаточно добавить классу Java аннотацию @javax.annotation.ManagedBean. Например:package com.sample.sales.core; + +import com.sample.sales.entity.Order; +import javax.annotation.ManagedBean; + +@ManagedBean(OrderWorker.NAME) +public class OrderWorker { + public static final String NAME = "sales_OrderWorker"; + + public void calculateTotals(Order order) { + } +} + Рекомендуется присваивать бину уникальное имя вида {имя_проекта}_{имя_класса}, и определять его в константе NAME. + Класс управляемого бина должен находиться внутри дерева пакетов с корнем, заданным в элементе context:component-scan файла + spring.xml + . В нашем случае файл sales-spring.xml содержит элемент:<context:component-scan base-package="com.sample.sales"/>что означает, что поиск аннотированных бинов для данного блока приложения будет происходить начиная с пакета com.sample.sales. + Если нужно обеспечить возможность подмены реализации, рекомендуется выделять бизнес-интерфейс бина, например следующим образом: + package com.sample.sales.core; + +import com.sample.sales.entity.Order; + +public interface OrderWorker { + String NAME = "sales_OrderWorker"; + + void calculateTotals(Order order); +} + package com.sample.sales.core; + +import com.sample.sales.entity.Order; +import javax.annotation.ManagedBean; + +@ManagedBean(OrderWorker.NAME) +public class OrderWorkerBean implements OrderWorker { + @Override + public void calculateTotals(Order order) { + } +} + Управляемые бины можно создавать на любом уровне, так как контейнер Spring Framework используется во всех стандартные блоках приложения. +
+
+ Использование бина + Ссылку на бин можно получить с помощью инжекции или класса AppBeans. В качестве примера использования бина рассмотрим реализацию сервиса OrderService, делегирующего выполнение бину OrderWorker:package com.sample.sales.core; + +import com.haulmont.cuba.core.Persistence; +import com.sample.sales.entity.Order; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.inject.Inject; + +@Service(OrderService.NAME) +public class OrderServiceBean implements OrderService { + + @Inject + protected Persistence persistence; + + @Inject + protected OrderWorker orderWorker; + + @Transactional + @Override + public BigDecimal calculateTotals(Order order) { + Order entity = persistence.getEntityManager().merge(order); + orderWorker.calculateTotals(entity); + } +} + В данном примере сервис стартует транзакцию, вносит полученный с клиентского уровня экземпляр сущности в персистентный контекст, и передает управление бину OrderWorker, который и содержит основную бизнес-логику. +
+
+
+ JMX-бины + Иногда требуется предоставить администратору системы возможность просматривать и изменять состояние некоторого управляемого бина во время выполнения. В этом случае рекомендуется создать JMX-бин - программный компонент, имеющий JMX-интерфейс. Такой бин как правило делегирует вызовы управляемому бину, содержащему кэш, конфигурационные данные или статистику, к которым нужно обеспечить доступ через JMX. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/JMXBeans.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Как видно из диаграммы, JMX-бин состоит из интерфейса и класса реализации. Класс должен представлять собой <link linkend="managed_beans">управляемый бин</link>, то есть иметь аннотацию <code>@ManagedBean</code> и уникальное имя. Интерфейс JMX-бина специальным образом регистрируется в <link linkend="spring.xml">spring.xml</link> для создания в текущей JVM собственно JMX-интерфейса.</para> + <para>Вызовы всех методов интерфейса JMX-бина перехватываются с помощью <application>Spring AOP</application> классом−<glossterm linkend="interceptor">интерцептором</glossterm> <methodname>MBeanInterceptor</methodname>, который обеспечивает установку правильного <code>ClassLoader</code> в контексте потока выполнения, и журналирование необработанных исключений.</para> + <warning> + <para>Интерфейс JMX-бина обязательно должен иметь имя вида <literal>{имя_класса}MBean</literal>.</para> + </warning> + <section> + <title>Создание JMX-бина + Рассмотрим процесс создания JMX-бина на примере. + + + Интерфейс JMX-бина:package com.sample.sales.core; + +import org.springframework.jmx.export.annotation.*; + +@ManagedResource(description = "Performs operations on Orders") +public interface OrdersMBean { + + @ManagedOperation(description = "Recalculates an order amount") + @ManagedOperationParameters({@ManagedOperationParameter(name = "orderId", description = "")}) + String calculateTotals(String orderId); +} + Интерфейс и его методы могут содержать аннотации для задания описания JMX-бина и его операций. Это описание будет отображаться во всех инструментах, работающих с данным JMX-интерфейсом, тем самым помогая администратору системы. + Так как инструменты JMX поддерживают ограниченный набор типов данных, параметры и результат метода желательно задавать типа String, и при необходимости выполнять конвертацию внутри метода. + + + Класс JMX-бина:package com.sample.sales.core; + +import com.haulmont.cuba.core.*; +import com.haulmont.cuba.core.app.*; +import com.sample.sales.entity.Order; +import org.apache.commons.lang.exception.ExceptionUtils; +import javax.annotation.ManagedBean; +import javax.inject.Inject; +import java.util.UUID; + +@ManagedBean("sales_OrdersMBean") +public class Orders implements OrdersMBean { + + @Inject + protected OrderWorker orderWorker; + + @Inject + protected Persistence persistence; + + @Authenticated + @Override + public String calculateTotals(final String orderId) { + try { + persistence.createTransaction().execute(new Transaction.Runnable() { + @Override + public void run(EntityManager em) { + Order entity = em.find(Order.class, UUID.fromString(orderId)); + orderWorker.calculateTotals(entity); + } + }); + return "Done"; + } catch (Throwable e) { + return ExceptionUtils.getStackTrace(e); + } + } +} + Аннотация @ManagedBean определяет, что данный класс является управляемым бином с именем sales_OrdersMBean. Имя указано напрямую в аннотации, а не в константе, так как доступ к JMX-бину из кода Java не требуется. + Рассмотрим реализацию метода calculateTotals(). + + Метод имеет аннотацию @Authenticated, т.е. при входе в метод и при отсутствии в потоке выполнения пользовательской сессии выполняется системная аутентификация. + + + Тело метода обернуто в блок try/catch, так что метод в случае успешного выполнения возвращает строку "Done", а в случае ошибки - stacktrace исключения в виде строки. + Следует иметь в виду, что в данном случае все исключения обрабатываются, а значит, не попадают в MBeanInterceptor и не выводятся в журнал автоматически. Поэтому при необходимости логгировать исключения здесь нужно добавить вызов логгера в секции catch. + + + Логика метода заключается в том, что он стартует транзакцию, загружает экземпляр сущности Order по идентификатору, и передает управление бину OrderWorker для обработки. + + + + + Регистрация JMX-бина в sales-spring.xml: + <bean id="sales_MBeanExporter" lazy-init="false" + class="com.haulmont.cuba.jmxcontrol.export.MBeanExporter"> + <property name="beans"> + <map> + <entry key="${cuba.webContextName}.sales:type=Orders" + value-ref="sales_OrdersMBean"/> + </map> + </property> +</bean> + + + Все JMX-бины проекта объявляются в одном экземпляре MBeanExporter в элементах map/entry свойства beans. Ключом элемента здесь является JMX ObjectName, значением - имя бина, заданное +в аннотации @ManagedBean. ObjectName начинается с имени веб-приложения, так как в одном экземпляре Tomcat (т.е. в одной JVM) может быть развернуто несколько веб-приложений, экспортирующих одинаковые JMX-интерфейсы. +
+
+ JMX-бины платформы + В данном разделе описаны некоторые имеющиеся в платформе JMX-бины. +
+ CachingFacadeMBean + CachingFacadeMBean предоставляет методы очистки различных кэшей в блоках Middleware и Web Client. + JMX ObjectName: app-core.cuba:type=CachingFacade и app.cuba:type=CachingFacade +
+
+ ConfigStorageMBean + ConfigStorageMBean позволяет просматривать и задавать значения свойствам приложения в блоках Middleware, Web Client и Web Portal. + Следует иметь в виду, что измененные значения для свойств, хранящихся в файлах, не сохраняются, и действуют только до рестарта данного блока. + JMX ObjectName: app-core.cuba:type=ConfigStorage, app.cuba:type=ConfigStorage, app-portal.cuba:type=ConfigStorage +
+
+ PersistenceManagerMBean + PersistenceManagerMBean предоставляет следующие возможности: + + управление механизмом статистики сущностей + + + отображение новых скриптов обновления БД методом findUpdateDatabaseScripts() и запуск обновления методом updateDatabase() + + + запуск произвольных JPQL запросов в контексте Middleware методами jpqlLoadList(), jpqlExecuteUpdate() + + + JMX ObjectName: app-core.cuba:type=PersistenceManager +
+
+ ScriptingManagerMBean + ScriptingManagerMBean является JMX-фасадом для интерфейса инфраструктуры + Scripting + . + JMX ObjectName: app-core.cuba:type=ScriptingManager + JMX атрибуты: + + RootPath - абсолютный путь к конфигурационному каталогу блока приложения, в котором запущен данный бин + + + JMX операции: + + runGroovyScript() - выполнить скрипт Groovy в контексте Middleware и вернуть результат. Для отображения в JMX-интерфейсе результат должен быть типа String. В остальном аналогичен методу Scripting.runGroovyScript(). + + +
+
+
+
+ Системная аутентификация + При выполнении пользовательских запросов программному коду Middleware через интерфейс + UserSessionSource + всегда доступна информация о текущем пользователе. Это возможно потому, что при получении запроса с клиентского уровня в потоке выполнения автоматически устанавливается соответствующий объект + SecurityContext + . + Однако существуют ситуации, когда текущий поток выполнения не связан ни с каким пользователем системы: например, при вызове метода бина из планировщика, либо через JMX-интерфейс. Если при этом бин выполняет изменение сущностей в базе данных, то ему потребуется информация о том, кто выполняет изменения, то есть аутентификация. + Такого рода аутентификация называется системной, так как не требует участия пользователя - средний слой приложения просто создает (или использует имеющуюся) пользовательскую сессию, и устанавливает в потоке выполнения соответствующий объект SecurityContext. + Обеспечить системную аутентификацию некоторого участка кода можно следующими способами: + + явно используя бин com.haulmont.cuba.security.app.Authentication, например:@Inject +protected Authentication authentication; +... +authentication.begin(); +try { + // authenticated code +} finally { + authentication.end(); +} + + + добавив методу бина аннотацию @Authenticated, например:@Authenticated +public String foo(String value) { + // authenticated code +} + + + Во втором случае также используется бин Authentication, но неявно, через интерцептор AuthenticationInterceptor, который перехватывает вызовы всех методов бинов с аннотацией @Authenticated. + В приведенных примерах пользовательская сессия будет создаваться от лица пользователя, логин которого указан в свойстве приложения + cuba.jmxUserLogin + . Если требуется аутентификация от имени другого пользователя, нужно воспользоваться первым вариантом и передать в метод begin() логин нужного пользователя. + + Если в момент выполнения Authentication.begin() в текущем потоке выполнения присутствует активная пользовательская сессия, то она не заменяется - соответственно, код, требующий аутентификации, будет выполняться с имеющейся сессией, и последующий метод end() не будет очищать поток. + Следует иметь в виду, что вызов метода JMX-бина из встроенной в Web Client консоли JMX является обычной обработкой пользовательского запроса. Это означает, что метод JMX-бина будет выполнен от имени текущего зарегистрированного в системе пользователя, независимо от наличия системной аутентификации. + +
+
+ Слой ORM +
+ Общие сведения + Object-Relational Mapping - объектно-реляционное отображение - технология связывания таблиц реляционной базы данных с объектами языка программирования. + + + Преимущества использования ORM: + + + + Позволяет работать с данными реляционной СУБД, манипулируя объектами Java + + + Упрощает программирование, избавляя от рутины написания тривиальных SQL-запросов + + + Упрощает программирование, позволяя извлекать и сохранять целые графы объектов одной командой + + + Обеспечивает легкое портирование приложения на различные СУБД + + + Использует лаконичный язык запросов JPQL + + + Оптимизирует количество выполняемых SQL-запросов на команды insert и update + + + + + + Недостатки: + + + + Требует понимания особенностей работы с ORM + + + Не позволяет напрямую оптимизировать SQL или использовать особенности применяемой СУБД + + + + + + В платформе CUBA используется реализация ORM по стандарту Java Persistence API на основе фреймворка Apache OpenJPA. +
+
+ EntityManager + EntityManager - основной интерфейс ORM, служит для управления персистентными сущностями. + + Ссылку на EntityManager можно получить через интерфейс Persistence, вызовом метода getEntityManager(). +Полученный экземпляр EntityManager привязан к текущей транзакции, то есть все вызовы getEntityManager() в рамках одной транзакции возвращают один и тот же экземпляр EntityManager. После завершения транзакции обращения к данному экземпляру невозможны. + + Экземпляр EntityManager содержит в себе "персистентный контекст" – набор экземпляров сущностей, загруженных из БД или только что созданных. Персистентный контекст является своего рода кэшем данных в рамках транзакции. +EntityManager автоматически сбрасывает в БД все изменения, сделанные в его персистентном контексте, в момент коммита транзакции, либо при явном вызове метода flush(). + Интерфейс EntityManager, используемый в CUBA-приложениях, в основном повторяет стандартный javax.persistence.EntityManager. Рассмотрим специфичные методы: + + setView() - устанавливает представление, с которым будет производиться последующая загрузка сущностей методом find() либо JPQL запросами. В результате энергично загружены будут все не-lazy атрибуты представления. + Если в данный метод передать null, либо не вызывать его вообще, загрузка будет производиться в соответствие с правилами аннотаций сущностей. + + + addView() - аналогичен методу setView(), но в случае наличия уже установленного в EntityManager представления, не заменяет его, а добавляет атрибуты переданного представления. + + + fetch() - обеспечивает для экземпляра сущности загрузку всех атрибутов указанного представления, включая lazy атрибуты. Экземпляр сущности должен быть в Managed состоянии. + Данный метод рекомендуется вызывать перед коммитом транзакции, если представление содержит lazy атрибуты, а экземпляр сущности нужно отправить на клиентский уровень. В этом случае только после вызова fetch() можно быть уверенным, что все нужные клиентсткому коду атрибуты действительно загружены. + + + isSoftDeletion() - проверяет, находится ли данный EntityManager в режиме мягкого удаления. + + + setSoftDeletion() - устанавливает режим мягкого удаления для данного экземпляра EntityManager. + + + getConnection() - возвращает java.sql.Connection, через который выполняет запросы данный экземпляр EntityManager, и, соответственно, текущая транзакция. Закрывать такое соединение не нужно, оно будет закрыто при завершении транзакции. + + + getDelegate() - возвращает javax.persistence.EntityManager, предоставляемый реализацией ORM. + + +
+
+ Состояния сущности + + + New + + Только что созданный в памяти экземпляр, например: Car car = new Car() + Новый экземпляр может быть передан в EntityManager.persist() для сохранения в БД, при этом он переходит в состояние Managed. + + + + Managed + + Загруженный из БД или новый, переданный в EntityManager.persist(), экземпляр. Принадлежит некоторому экземпляру EntityManager, другими словами, находится в его персистентном контексте. + Любые изменения экземпляра в состоянии Managed будут сохранены в БД в случае коммита транзакции, к которой принадлежит данный EntityManager + + + + Detached + + Экземпляр, загруженный из БД и отсоединенный от своего персистентного контекста (вследствие закрытия транзакции или сериализации). + Изменения, вносимые в Detached экземпляр, запоминаются в самом этом экземпляре (в полях, добавленных с помощью bytecode enhancement). +Эти изменения будут сохранены в БД, только если данный экземпляр будет снова переведен в состояние Managed путем передачи в метод EntityManager.merge(). + Метод merge() выполняет следующее: загружает из БД экземпляр с тем же идентификатором, переносит в него состояние переданного Detached экземпляра и возвращает загруженный Managed экземпляр. Далее надо работать именно с возвращенным Managed экземпляром. + + + +
+
+ Загрузка по требованию + Загрузка по требованию (lazy loading) позволяет загружать связанные сущности отложенно, т.е. только в момент первого обращения к их свойствам. + Загрузка по требованию в сумме порождает больше запросов к БД, чем энергичная загрузка (eager fetching), однако нагрузка при этом растянута во времени. + + Например, при извлечении списка N экземпляров сущности A, содержащих ссылку на экземпляр сущности B, в случае загрузки по требованию будет выполнено N+1 запросов к базе данных. + + + Для минимизации времени отклика и снижения нагрузки необходимо стремиться к меньшему количеству обращений к БД. Для этого в платформе используется механизм представлений, с помощью которого в вышеописанном случае ORM может сформировать один запрос к БД с объединением таблиц. + + + Если A содержит коллекцию B, в случае энергичной загрузки ORM сформирует SQL запрос, возвращающий произведение строк A и B. + + + Иногда загрузка по требованию с точки зрения производительности предпочтительнее, чем энергичная загрузка. Например, когда работает асинхронный процесс, выполняющий некоторую бизнес-логику, общее время выполнения некритично и желательно распределить во времени нагрузку на БД. + + + Загрузка по требованию работает только для экземпляра в состоянии Managed, то есть внутри транзакции, загрузившей данный экземпляр. +
+
+ Выполнение JPQL запросов + Для выполнения JPQL запросов предназначен интерфейс Query, ссылку на который который можно получить у текущего экземпляра EntityManager вызовом метода createQuery(). Если запрос предполагается использовать для извлечения сущностей, рекомендуется вызывать createQuery() с передачей типа результата, что приведет к созданию TypedQuery. + Методы Query в основном соответствуют методам стандартного интерфейса + javax.persistence.Query + . Рассмотрим отличия. + + setParameter() - устанавливает значение параметра запроса. При передаче в данный метод экземпляра сущности выполняет неявное преобразование экземпляра в его идентификатор. Например:Customer customer = ...; +TypedQuery<Order> query = entityManager.createQuery( + "select o from sales$Order o where o.customer.id = ?1", Order.class); +query.setParameter(1, customer); + Обратите внимание на сравнение в запросе по идентификатору, но передачу в качестве параметра самого экземпляра сущности. + Вариант метода с передачей implicitConversions = false не выполняет такого преобразования. + + + setView(), addView() - аналогичны одноименным методам интерфейса EntityManager - устанавливают представление, используемое при загрузке данных текущим запросом, не влияя на представление всего EntityManager. + + + getDelegate() - возвращает экземпляр javax.persistence.Query, предоставляемый реализацией ORM. + + +
+ Поиск like без учета регистра + Для удобного формирования условия поиска без учета регистра символов и по любой части строки можно использовать префикс (?i) имени параметра запроса, например:select c from sales$Customer c where c.name like :(?i)name + В данном случае ORM переведет значение параметра name в нижний регистр и обрамит символами %, а затем в БД выполнит SQL с условием вида lower(C.NAME) like ? +
+
+ Макросы в JPQL + Текст JPQL запроса может включать макросы, которые обрабатываются перед выполнением и превращаются в исполняемый JPQL, дополнительно модифицируя набор параметров. + Макросы, определенные в платформе, решают следующие задачи: + + Позволяют обойти принципиальную невозможность средствами JPQL выразить условие зависимости значения поля от текущего момента времени (не работает арифметика типа current_date-1) + + + Позволяют сравнивать с датой поля типа Timestamp (содержащие дату+время) + + + Рассмотрим их подробно: + + @between + + Имеет вид @between(field_name, moment1, moment2, time_unit), где + + field_name - имя атрибута для сравнения + + + moment1, moment2 - моменты времени, в которые должно попасть значение атрибута field_name. Каждый из моментов должен быть определен выражением с участием переменной now, к которой может быть прибавлено или отнято целое число + + + time_unit - определяет единицу измерения времени, которое прибавляется или вычитается из now в выражениях моментов, а также точность округления моментов. Может быть следующим: year, month, day, hour, minute, second. При включенном базовом проекте workflow можно также использовать единицы рабочего времени: workday, workhour, workminute. + + + Макрос преобразуется в следующее выражение JPQL: field_name >= :moment1 and field_name < :moment2 + Пример 1. Покупатель создан сегодня:select c from sales$Customer where @between(c.createTs, now, now+1, day) + Пример 2. Покупатель создан в течение последних 10 минут:select c from sales$Customer where @between(c.createTs, now-10, now, minute) + Пример 3. Документы, датированные последними 5 рабочими днями (для проектов, включающих workflow): select d from sales$Doc where @between(d.createTs, now-5, now, workday) + + + + @today + + Имеет вид @today(field_name) и обеспечивает формирование условия попадания значения атрибута в текущий день. По сути это частный случай макроса @between. + Пример. + +Пользователь создан сегодня: select d from sales$Doc where @today(d.createTs) + + + + @dateEquals + + Имеет вид @dateEquals(field_name, parameter) и позволяет сформировать условие попадания значения поля field_name типа Timestamp в дату, задаваемую параметром parameter. + Пример:select d from sales$Doc where @dateEquals(d.createTs, :param) + + + + @dateBefore + + Имеет вид @dateBefore(field_name, parameter) и позволяет сформировать условие, что дата значения поля field_name типа Timestamp меньше даты, задаваемой параметром parameter. + Пример:select d from sales$Doc where @dateBefore(d.createTs, :param) + + + + @dateAfter + + Имеет вид @dateAfter(field_name, parameter) и позволяет сформировать условие, что дата значения поля field_name типа Timestamp больше или равна дате, задаваемой параметром parameter. + Пример:select d from sales$Doc where @dateAfter(d.createTs, :param) + + + + Список макросов может быть расширен в прикладном проекте. Для создания нового макроса необходимо определить бин, реализующий интерфейс QueryMacroHandler, и задать ему @Scope("prototype"). Механизм выполнения JPQL запросов создает все доступные бины типа QueryMacroHandler, и по очереди передает им текст запроса с набором параметров. Очередность вызова обработчиков не определена. +
+
+
+
+ Управление транзакциями +
+ Программное управление транзакциями + Программное управление транзакциями осуществляется с помощью интерфейса com.haulmont.cuba.core.Transaction, ссылку на который можно получить методами createTransaction() или getTransaction() интерфейса инфраструктуры + Persistence + . + Метод createTransaction() создает новую транзакцию и возвращает интерфейс Transaction. Последующие вызовы методов commit(), commitRetaining(), end() этого интерфейса управляют созданной транзакцией. Если в момент создания существовала другая транзакция, то она будет приостановлена, и возобновлена после завершения созданной. + Метод getTransaction() вызывает либо создание новой, либо присоединение к текущей транзакции. Если в момент вызова существовала активная транзакция, то метод успешно завершается, и последующие вызовы commit(), commitRetaining(), end() не оказывают никакого влияния на существующую транзакцию. Однако если end() вызван без предварительного вызова commit(), то текущая транзакция помечается как RollbackOnly. + Пример ручного управления транзакцией:@Inject +private Persistence persistence; +... +Transaction tx = persistence.createTransaction(); +try { + EntityManager em = persistence.getEntityManager(); + Customer customer = new Customer(); + customer.setName("John Smith"); + em.persist(customer); + + tx.commit(); +} finally { + tx.end(); +} + Интерфейс Transaction имеет также метод execute(), принимающий на вход класс-действие, которое нужно выполнить в данной транзакции. Это позволяет организовать управление транзакциями в функциональном стиле, например:persistence.createTransaction().execute(new Transaction.Runnable() { + public void run(EntityManager em) { + // transactional code here + } +}); + Если транзакционный блок должен вернуть результат, класс-действие должен реализовывать интерфейс Transaction.Callable. Если результат не требуется, как в приведенном примере, то класс-действие удобно наследовать от абстрактного класса Transaction.Runnable. + Следует иметь в виду, что метод execute() у некоторого экземпляра Transaction можно вызвать только один раз, так как после выполнения кода класса-действия транзакция завершается. +
+
+ Декларативное управление транзакциями + Любой метод управляемого бина Middleware можно пометить аннотацией @org.springframework.transaction.annotation.Transactional, что вызовет автоматическое создание транзакции при вызове этого метода. В таком методе не нужно вызывать Persistence.createTransaction(), а можно сразу получать EntityManager и работать с ним. + Для аннотации @Transactional можно указать параметры. Основным параметром является режим создания транзакции - Propagation. Значение REQUIRED соответствует getTransaction(), значение REQUIRES_NEW - createTransaction(). По умолчанию REQUIRED. + + Декларативное управление транзакциями позволяет уменьшить количество boilerplate кода, однако имеет следующий недостаток: коммит транзакции проиходит вне прикладного кода, что часто затрудняет отладку, т.к. скрывается момент отправки изменений в БД и перехода сущностей в состояние Detached. Кроме того, следует иметь в виду, что декларативная разметка сработает только в случае вызова метода контейнером, т.е. вызов транзакционного метода из другого метода того же самого объекта не приведет к старту транзакции. + + В связи с этим рекомендуется применять декларативное управление транзакциями только для простых случаев типа метода сервиса, читающего некоторый объект и возвращающего его на клиента. +
+
+ Примеры взаимодействия транзакций +
+ Откат вложенной транзакции + Если вложенная транзакция создана через getTransaction(), то ее откат приведет к невозможности коммита охватывающей транзакции. Например:void methodA() { + Transaction tx = persistence.createTransaction(); + try { + // (1) вызываем метод, создающий вложенную транзакцию + methodB(); + + // (4) в этот момент будет выброшено исключение, т.к. транзакция + // помечена как rollback only + tx.commit(); + } finally { + tx.end(); + } +} + +void methodB() { + Transaction tx = persistence.getTransaction(); + try { + // (2) допустим здесь возникло исключение + tx.commit(); + } catch (Exception e) { + // (3) обрабатываем его и выходим + return; + } finally { + tx.end(); + } +} + Если же транзакция в methodB() будет создана через createTransaction(), то ее откат не окажет никакого влияния на коммит охватывающей транзакции в methodA(). +
+
+ Чтение и изменение данных во вложенной транзакции + Рассмотрим сначала зависимую вложенную транзакцию, создаваемую через getTransaction():void methodA() { + Transaction tx = persistence.createTransaction(); + try { + EntityManager em = persistence.getEntityManager(); + + // (1) загружаем сущность, в которой name == "old name" + Employee employee = em.find(Employee.class, id); + assertEquals("old name", employee.getName()); + + // (2) присваиваем новое значение полю + employee.setName("name A"); + + // (3) вызываем метод, создающий вложенную транзакцию + methodB(); + + // (8) здесь происходит коммит изменений в БД, и в ней + // окажется значение "name B" + tx.commit(); + + } finally { + tx.end(); + } +} + +void methodB() { + Transaction tx = persistence.getTransaction(); + try { + // (4) получаем тот же экземпляр EntityManager, что и methodA + EntityManager em = persistence.getEntityManager(); + + // (5) загружаем сущность с тем же идентификатором + Employee employee = em.find(Employee.class, id); + + // (6) значение поля новое, т.к. мы работаем с тем же + // персистентным контекстом, и обращения к БД вообще + // не происходит + assertEquals("name A", employee.getName()); + employee.setName("name B"); + + // (7) в этот момент реально коммита не происходит + tx.commit(); + } finally { + tx.end(); + } +} + Теперь рассмотрим тот же самый пример с независимой вложенной транзакцией, создаваемой через createTransaction(): void methodA() { + Transaction tx = persistence.createTransaction(); + try { + EntityManager em = persistence.getEntityManager(); + + // (1) загружаем сущность, в которой name == "old name" + Employee employee = em.find(Employee.class, id); + assertEquals("old name", employee.getName()); + + // (2) присваиваем новое значение полю + employee.setName("name A"); + + // (3) вызываем метод, создающий вложенную транзакцию + methodB(); + + // (8) здесь возникнет исключение из-за оптимистичной блокировки + // и коммит не пройдет вообще + tx.commit(); + + } finally { + tx.end(); + } +} + +void methodB() { + Transaction tx = persistence.createTransaction(); + try { + // (4) создается новый экземпляр EntityManager, т.к. это + // новая транзакция + EntityManager em = persistence.getEntityManager(); + + // (5) загружаем сущность с тем же идентификатором + Employee employee = em.find(Employee.class, id); + + // (6) значение поля старое, т.к. произошла загрузка из БД + // старого экземпляра сущности + assertEquals("old name", employee.getName()); + + employee.setName("name B"); + + // (7) здесь происходит коммит изменений в БД, и в ней + // окажется значение "name B" + tx.commit(); + + } finally { + tx.end(); + } +} + В последнем случае исключение в точке (8) возникнет только если сущность является оптимистично блокируемой, т.е. если она реализует интерфейс Versioned. +
+
+
+ Таймаут транзакции + Для создаваемой транзакции может быть указан таймаут в секундах, при превышении которого транзакция будет прервана и откачена. Таймаут транзакции ограничивает максимальную длительность запросов к базе данных. + При программном управлении транзакциями таймаут включается путем передачи объекта TransactionParams в метод Persistence.createTransaction(). Например:Transaction tx = persistence.createTransaction(new TransactionParams().setTimeout(2)); + При декларативном управлении транзакциями используется параметр timeout аннотации @Transactional, например:@Transactional(timeout = 2) +public void someServiceMethod() { +... + Таймаут по умолчанию может быть задан в свойстве приложения + cuba.defaultQueryTimeoutSec + . +
+ Особенности реализации для различных СУБД + PostgreSQL + К сожалению, JDBC драйвер PostgreSQL не поддерживает метод setQueryTimeout() интерфейса java.sql.Statement, поэтому в начале каждой транзакции, для которой определен таймаут (любым способом, включая ненулевое значение свойства + cuba.defaultQueryTimeoutSec + ), выполняется дополнительный оператор в БД: set local statement_timeout to {value}. При этом в случае превышения таймаута запрос будет прерван самим сервером БД. + Для снижения нагрузки от этих дополнительных операторов рекомендуется поступать следующим образом: + + Таймаут по умолчанию устанавливать не на Middleware с помощью свойства cuba.defaultQueryTimeoutSec, в на самом сервере PostgreSQL в файле postgresql.conf, например statement_timeout = 3000 (это в миллисекундах). + + + Для методов, которым требуется большее время таймаута (отчеты и пр.), явно указывать желаемый таймаут в параметрах транзакции. + + + Microsoft SQL Server + Драйвер JTDS поддерживает метод setQueryTimeout() интерфейса java.sql.Statement, поэтому для EntityManager просто устанавливается стандартное свойство javax.persistence.query.timeout, которое соответствующим образом влияет на JDBC запросы. +
+
+
+
+ DataService и DataWorker + Управляемый бин DataWorker является универсальным средством для загрузки графов сущностей из базы данных, и для сохранения изменений, произведенных в Detached экземплярах сущностей. Сервис DataService является фасадом для вызова DataWorker с клиентского уровня приложения. + Выделение бизнес-логики загрузки и сохранения в DataWorker дает возможность при необходимости создать свой сервис, делегирующий основную работу DataWorker и выполняющий дополнительные преобразования, например перед возвратом данных на клиентский уровень. + DataWorker всегда стартует новую транзакцию и по завершению работы выполняет коммит, таким образом возвращая сущности в состоянии Detached. + Методы DataWorker: + + load(), loadList() - загружает граф сущностей в сответствии с параметрами переданного объекта LoadContext. + Данные методы проверяют наличие у пользователя права EntityOp.READ на загружаемую сущность. Кроме того, при извлечении сущностей из БД накладываются ограничения групп доступа (см. руководство Платформа CUBA. Подсистема безопасности). Для отмены действия ограничений в текущем запросе можно передать в LoadContext атрибут useSecurityConstraints = false. + + + commit() - сохраняет в базе данных набор сущностей, переданный в объекте CommitContext. Возвращает набор экземпляров сущностей, возвращенных из метода EntityManager.merge(), то есть по сути свежие экземпляры, только что обновленные в БД. Дальнейшая работа должна производиться именно с этими возвращенными экземплярами, чтобы предотвратить потерю данных или исключения оптимистичной блокировки. + Данный метод проверяет наличие у пользователя права EntityOp.UPDATE на изменяемые сущности, и EntityOp.DELETE на удаляемые. + + + commitNotDetached() - аналогичен методу commit(), но предназначен для сохранения в БД экземпляров, у которых отсутствует информация о Detached состоянии. Такие экземпляры сущностей могут быть переданы с клиентов, которые работают не напрямую с объектами, загруженными Middleware, а с дополнительными Data Transfer Objects, либо вообще не с Java объектами, а с их XML или JSON представлением (как например клиенты REST API) + + + В процессе загрузки данных DataWorker может реализовывать дополнительную функциональность, описанную ниже. +
+ Запросы с distinct + В JPQL запросах для экранов со списками сущностей, в которых включено постраничное отображение и возможна непредсказуемая модификация запроса универсальным фильтром или механизмом ограничений групп доступа, при отсутствии в запросе оператора distinct может возникать следующий эффект: + + при объединении с коллекцией на уровне извлечения из базы данных возникает набор с дубликатами строк + + + на клиентском уровне в источнике данных дубликаты исчезают, т.к. попадают в мэп (java.util.Map) + + + при постраничном отображении на одной странице оказывается меньшее количество строк чем запрошено, общее количество строк наоборот завышено. + + + Таким образом, рекомендуется в JPQL запросы браузеров включать предложение distinct, которое гарантирует отсутствие дубликатов записей при выборке из базы данных. Однако в некоторых серверах БД (в частности PostgreSQL) при большом количестве извлекаемых записей (более 10000) SQL запрос с distinct выполняется недопустимо долго. + Для решения этой проблемы в платформе реализована возможность корректной работы без distinct на уровне SQL. Данный механизм включается свойством приложения + cuba.inMemoryDistinct + , при активации которого выполняется следующее: + + В JPQL запросе должен по прежнему присутствовать select distinct + + + В DataWorker из JPQL запроса перед отправкой в ORM distinct вырезается + + + После загрузки страницы данных на Middleware удаляются дубликаты и выполняются дополнительные запросы к БД для получения нужного количества строк, которые затем и возвращаются клиенту. + + +
+
+ Последовательная выборка + DataWorker может выполнять последовательную выборку данных из результатов предыдущего запроса. Эта возможность используется в универсальном фильтре при последовательном наложении фильтров. + Данный механизм работает следующим образом: + + При получении LoadContext с установленными атрибутами prevQueries и queryKey DataWorker выполняет выборку по предыдущему запросу и сохраняет идентификаторы полученных сущностей в таблице SYS_QUERY_RESULT (соответствующей сущности sys$QueryResult), разделяя наборы записей по идентификаторам пользовательских сессий и ключу сеанса выборки queryKey. + + + Текущий запрос модифицируется для объединения с результатами предыдущего, так что в итоге возвращает данные, соответствующие условиям обоих запросов, объединенных по "И". + + + Далее процесс может повторяться, при этом уменьшающийся набор предыдущих результатов удаляется из таблицы SYS_QUERY_RESULT и заполняется заново. + + + Таблицу SYS_QUERY_RESULT необходимо периодически чистить от ненужных результатов запросов, оставленных завершенными пользовательскими сессиями. Для этого предназначен метод deleteForInactiveSessions бина QueryResultsManagerAPI. В прикладном проекте с включенным параметром + cuba.allowQueryFromSelected + необходимо вызывать этот метод либо из назначенных заданий, либо из стандартного планировщика Spring, например:<task:scheduled-tasks scheduler="scheduler"> + <task:scheduled ref="cuba_QueryResultsManager" method="deleteForInactiveSessions" fixed-rate="600000"/> +</task:scheduled-tasks> +
+
+
+ Работа с СУБД +
+ Выбор и подключение + Тип используемой СУБД определяется свойством приложения + cuba.dbmsType + и настройкой источника данных. Конфигурационный файл для Tomcat, определяющий источник данных, описан в + + Обратите внимание на применимость СУБД в зависимости от используемых базовых проектов платформы: + + HSQLDB - применяется только для запуска интеграционных тестов платформы, не используется в прикладных проектах + + + PostgreSQL - поддерживается всеми базовыми проектами платформы + + + Microsoft SQL Server - поддерживается базовыми проектами cuba, workflow, fts + + + +
+
+ Создание и обновление БД + Проект CUBA-приложения всегда содержит два набора SQL скриптов: + + Скрипты создания БД - предназначены для создания базы данных с нуля. + Скрипты создания располагаются в каталоге /db/init модуля core. Для каждого типа СУБД, поддерживаемой приложением, создается свой набор скриптов и располагается в подкаталоге с именем, соответствующим значению перечисления DbmsType, например /db/init/postgres. Имена скриптов создания должны иметь вид {optional_prefix}create-db.sql. + + + Скрипты обновления БД - предназначены для поэтапного приведения структуры БД к текущему состоянию модели данных. + Скрипты обновления располагаются в каталоге /db/update модуля core. Для каждого типа СУБД, поддерживаемой приложением, создается свой набор скриптов и располагается в подкаталоге с именем, соответствующим значению перечисления DbmsType, например /db/update/postgres. + Скрипты обновления должны иметь имена, которые при сортировке в алфавитном порядке образуют правильную последовательность их выполнения (обычно это хронологическая последовательность их создания). Поэтому рекомендуется задавать имя скрипта обновления в виде {yymmdd}-{description}.sql, где yy - год, mm - месяц, dd - день, description - краткое описание скрипта. Например 121003-addCodeToCategoryAttribute.sql. + Скрипты обновления можно группировать в подкаталоги, главное чтобы путь к скрипту с учетом подкаталога не нарушал хронологической последовательности. Например, можно создавать подкаталоги по номеру года или по году и месяцу. + + + В развернутом приложении скрипты создания и обновления БД располагаются в специальном каталоге скриптов базы данных, задаваемым свойством приложения + cuba.dbDir + . + Скрипты представляют собой текстовые файлы с набором DDL и DML команд, разделенных символом "^". Символ "^" применяется для того, чтобы можно было применять разделитель ";" в составе сложных команд, например при создании функций или триггеров. Встроенный механизм исполнения скриптов разделяет входной файл на команды по разделителю "^" и выполняет каждую команду в отдельной транзакции. Это означает, что при необходимости можно сгруппировать несколько простых операторов (например insert), разделенных точкой с запятой, и обеспечить выполнение их в одной транзакции. +
+ Создание БД + Базу данных можно создать двумя способами: с помощью скрипта сборки Gradle или на старте приложения путем запуска автоматического обновления. + Скрипт сборки build.gradle содержит задачу createDb, которая создает указанную в параметрах задачи БД и выполняет на ней все SQL скрипты создания (базовых проектов и самого приложения). + + Если база данных по указанному в скрипте сборки URL существует, она полностью удаляется и создается заново. + + Чтобы инициализировать БД механизмом автоматического обновления (например в развернутом приложении при отсутствии скрипта сборки), нужно выполнить следующее: + + включить свойство приложения + cuba.automaticDatabaseUpdate + + + + создать пустую базу данных, соответствующую URL, заданному в описании источника данных в + context.xml + + + + запустить веб-сервер, содержащий блок Middleware. На старте приложения БД будет проинициализирована и сразу же готова к работе. + + +
+
+ Обновление БД + Процесс обновления базы данных заключается в выполнении скриптов обновления, присутствующих в проекте или в каталоге скриптов, которые не зарегистрированы как выполненные в таблице SYS_DB_CHANGELOG. Эти скрипты могут быть выполнены как вручную (используя сторонние инструменты), так и следующими встроенными в систему способами: + + Выполнением задачи updateDb скрипта сборки build.gradle. + + + В работающем приложении: + + на старте Middleware при включенном механизме автоматического обновления + + + после старта вызовом метода updateDatabase() JMX-бина + PersistenceManagerMBean + + + + + +
+
+ Механизм автоматического обновления + Механизм автоматического обновления включается свойством приложения + cuba.automaticDatabaseUpdate + . Он активируется на старте блока Middleware до момента полной инициализации приложения, и действует следующим образом: + + Если в БД отсутствует таблица SEC_USER, то считается, что база данных пуста, и запускается полная инициализация с помощью скриптов создания БД. После выполнения инициализирующих скриптов их имена запоминаются в таблице SYS_DB_CHANGELOG. Кроме того, там же сохраняются имена всех доступных скриптов обновления, без их выполнения. + + + Если в БД имеется таблица SEC_USER, но отсутствует таблица SYS_DB_CHANGELOG (это случай, когда в первый раз запускается описываемый механизм на имеющейся рабочей БД), никакие скрипты не запускаются. Вместо этого создается таблица SYS_DB_CHANGELOG и в ней сохраняются имена всех доступных на данный момент скриптов обновления. + + + Если в БД имеются и таблица SEC_USER и таблица SYS_DB_CHANGELOG, производится запуск скриптов обновления, и их имена запоминаются в таблице SYS_DB_CHANGELOG. Причем запускаются только те скрипты, имен которых не было в таблице SYS_DB_CHANGELOG, т.е. не запускавшиеся ранее. +Последовательность запуска скриптов определяется 2-мя факторами: приоритетом базового проекта (см. содержимое каталога скриптов базы данных: 10-cuba, 20-workflow, ...) и именем файла скрипта (с учетом подкаталогов внутри каталога update) в алфавитном порядке. + + +
+
+
+ Проектирование БД +
+ Соответствие типов данных + В таблице приведено соответствие типов данных, отличающихся для разных СУБД. + + + + + + + + Java + PostgreSQL + MS SQL Server + + + + + UUID + uuid + uniqueidentifier + + + Date + timestamp + datetime + + + Boolean + boolean + tinyint + + + byte[] + bytea + image + + + + +
+
+ Особенности MS SQL Server + Microsoft SQL Server использует кластерные индексы для таблиц. + По умолчанию кластерный индекс создается по первичному ключу таблицы, однако используемые в CUBA-приложении ключи типа UUID плохо подходят для кластерного индекса. Поэтому необходимо для каждой таблицы правильно выбрать и создать кластерный индекс. Поле для кластерного индекса должно быть небольшим и монотонно возрастающим, поэтому ориентировочные правила следующие: + + Для большинства таблиц подходит поле CREATE_TS. При этом записи будут физически располагаться в порядке их создания. + + + Для композитных сущностей, если чтение превалирует над записью, имеет смысл использовать ссылку на владельца. При этом записи будут сгруппированы по владельцам, и их извлечение вместе с владельцем будет происходить быстрее. + + + Для небольших (< 100 записей) редко изменяемых таблиц тип кластерного индекса не важен, можно оставить ID. + + + Для таблиц сущностей, унаследованных по стратегии JOINED, в которых нет поля CREATE_TS, нужно создать его искусственно с параметром default current_tmestamp. + + + Пример:create table SALES_CUSTOMER ( + ID uniqueidentifier not null, + CREATE_TS datetime, + ... + primary key nonclustered (ID) +)^ + +create clustered index IDX_SALES_CUSTOMER_CREATE_TS on SALES_CUSTOMER (CREATE_TS)^ + Пример композитной сущности: create table SALES_ITEM ( + ID uniqueidentifier not null, + CREATE_TS datetime, + ... + ORDER_ID uniqueidentifier, + ... + primary key nonclustered (ID), + constraint FK_SALES_ITEM_ORDER foreign key (ORDER_ID) references SALES_ORDER(ID) +)^ + +create clustered index IDX_SALES_ITEM_ORDER on SALES_ITEM (ORDER_ID)^ + Пример унаследованной сущности: create table SALES_DOC ( + CARD_ID uniqueidentifier, + CREATE_TS datetime default current_timestamp, + NUMBER varchar(50), + primary key nonclustered (CARD_ID), + constraint FK_SALES_DOC_CARD foreign key (CARD_ID) references WF_CARD (ID) +)^ + +create clustered index IDX_SALES_DOC_CREATE_TS on SALES_DOC (CREATE_TS)^ + +create index IDX_SALES_DOC_CARD on SALES_DOC (CARD_ID)^ +
+
+
+
+
+ Универсальный пользовательский интерфейс + Подсистема универсального пользовательского интерфейса (Generic UI, GUI) позволяет разрабатывать экраны пользовательского интерфейса, используя XML и Java. Созданные таким образом экраны одинаково работоспособны в двух +стандартных клиентских блоках: Web Client и Desktop Client. +
+ Структура универсального пользовательского интерфейса + + + + + +
+ Здесь в центре изображены основные составляющие экранов универсального пользовательского интерфейса: + + XML-дескрипторы - файлы XML, содержащие информацию об источниках данных и компоновке +экрана + + + Контроллеры - классы Java, содержащие логику инициализации экрана +и обработки событий от элементов пользовательского интерфейса. + + + Код экранов приложения, расположенный в модуле gui, взаимодействует с интерфейсами визуальных +компонентов (VCL Interfaces), реализованными по-отдельности в модулях web и desktop базового проекта cuba. Для Web Client реализация основана на фреймворке Vaadin, для Desktop Client – на фреймворке Java Swing. + Библиотека визуальных компонентов (Visual Components Library, VCL) +содержит большой набор готовых компонентов для отображения данных. + Механизм источников данных (Datasources) предоставляет унифицированный +интерфейс, обеспечивающий +функционирование +связанных +с +данными визуальных компонентов. + Инфраструктура клиента (Infrastructure) включает в себя главное окно приложения, +механизмы отображения и взаимодействия экранов UI, а также средства +взаимодействия со средним слоем. +
+ Экраны + Экран универсального пользовательского интерфейса состоит из XML-дескриптора и класса контроллера. Дескриптор содержит ссылку на класс контроллера. + Для того, чтобы экран можно было вызывать из главного меню или из Java кода (например из контроллера другого экрана), XML-дескриптор должен быть зарегистрирован в файле + screens.xml + проекта. + Главное меню приложения формируется отдельно для Web Client и Desktop Client на основе файлов + menu.xml + , расположенных соответственно в модулях web и desktop проекта. +
+ XML-дескриптор + XML-дескриптор - это файл формата XML, описывающий источники данных и расположение визуальных компонентов экрана. + Схема XML доступна по адресу http://schemas.haulmont.com/cuba/4.0/window.xsd + Рассмотрим структуру дескриптора. + window − корневой элемент. + Атрибуты window: + + + class + − имя класса контроллера + + + messagesPackпакет сообщений данного экрана, который будет использован при получении локализованных строк без указания пакета из XML-дескриптора и из контроллера методом getMessage() + + + caption − заголовок экрана, может содержать ссылку на сообщение из вышеуказанного пакета, например, caption="msg://caption" + + + focusComponent − (необязательно) идентификатор компонента, который получит фокус ввода при отображении экрана. + + + Элементы window: + + metadataContext − опциональный элемент для инициализации представлений (views), необходимых данному экрану. Предпочтительным является определение всех представлений в одном общем файле + views.xml + , так как все описатели представлений разворачиваются в один общий репозиторий, и при рассредоточении описателей по разным файлам трудно обеспечить уникальность имен. + + + dsContext − элемент, определяющий источники данных данного экрана. + + + companions - опциональный элемент, задающий список классов-компаньонов данного контроллера + Элементы companions: + + web - задает компаньон, реализованный в модуле web + + + desktop - задает компаньон, реализованный в модуле desktop + + + Каждый из этих элементов содержит атрибут class, задающий класс компаньона. + + + layout − корневой элемент компоновки экрана. Является сам по себе контейнером с вертикальным расположением компонентов, аналогичным + vbox + . + Атрибуты layout: + + + spacing + + + + + expand + + + + + +
+
+ Контроллер экрана + Контроллер экрана - это Java или Groovy класс, связанный с XML-дескриптором, и содержащий логику инициализации и обработки событий экрана. + Контроллер должен быть унаследован от одного из следующих базовых классов: + + + AbstractFrame − реализует интерфейс IFrame и предназначен для реализации фреймов − многократно используемых компонентов экранов. + + + AbstractWindow − реализует интерфейc Window и может быть использован для реализации любых экранов. + + + AbstractLookup − реализует интерфейс Lookup и предназначен для реализации браузеров сущностей с возможностью выбора элемента списка для использования его в вызывающем экране. + + + AbstractEditor − реализует интерфейс Editor и предназначен для реализации экранов редактирования экземпляра сущности. + + + + Если экрану не нужна никакая дополнительная логика, то в качестве контроллера можно использовать сам базовый класс AbstractWindow, AbstractLookup или AbstractEditor, указав его в XML-дескрипторе (эти классы на самом деле не являются абстрактными в смысле невозможности создания экземпляров). Для фрейма класс контроллера можно не указывать вообще. + + Класс контроллера должен быть зарегистрирован в XML-дескрипторе экрана в атрибуте class корневого элемента window. +
+ Зависимости контроллеров + В контроллерах можно использовать Dependency Injection для получения ссылок на используемые объекты. Для этого нужно объявить либо поле соответствующего типа, либо метод доступа на запись (setter) с соответствующим типом результата, и добавить ему одну из следующих аннотаций: + + @Inject - простейший вариант, поиск объекта для инжекции будет произведен по типу поля/метода и по имени, эквивалентному имени поля либо имени атрибута (по правилам JavaBeans) для метода + + + @Named("someName") - вариант с явным указанием имени искомого объекта + + + Инжектировать в контроллеры можно следующие объекты: + + Визуальные компоненты данного экрана, определенные в XML-дескрипторе. Если тип атрибута унаследован от Component, в текущем экране будет произведен поиск компонента с соответствующим именем. + + + Действия, определенные в XML-дескрипторе - см. + + + Источники данных, определенные в XML-дескрипторе. Если тип атрибута унаследован от Datasource, в текущем экране будет произведен поиск источника данных с соответствующим именем. + + + UserSession. Если тип атрибута - + UserSession + , будет инжектирован объект текущей пользовательской сессии. + + + DsContext. Если тип атрибута - DsContext, будет инжектирован DsContext текущего экрана. + + + WindowContext. Если тип атрибута - WindowContext, будет инжектирован WindowContext текущего экрана. + + + DataService. Если тип атрибута - com.haulmont.cuba.gui.data.DataService, будет инжектирован этот объект. + + + Любой бин, определенный в контексте данного клиентского блока приложения, в том числе: + + импортируемые клиентом сервисы Middleware + + + ComponentsFactory + + + WindowConfig + + + ExportDisplay + + + + BackgroundWorker + + + + + + Если ничего из вышеперечисленного не подошло и контроллер имеет компаньонов, в случае совпадения типов будет инжектирован компаньон для текущего типа клиента. + + +
+
+ Методы контроллеров + Общим методом контроллеров всех типов является метод init(). Этот метод вызывается фреймворком после создания класса окна и всего дерева компонентов, описанного XML-дескриптором. Здесь контроллер может произвести инициализацию экрана перед его открытием (например, создать обработчики нажатий на кнопки). + В метод init() из вызывающего кода передается мэп параметров, которые могут быть использованы внутри контроллера. Эти параметры могут быть переданы как из кода контроллера вызывающего экрана (в методе openWindow()), так и установлены в файле регистрации экранов + screens.xml + . +
+
+ AbstractEditor + AbstractEditor − базовый класс контроллеров экранов редактирования экземпляра сущности. + При создании конкретного класса контроллера рекомендуется параметризовать AbstractEditor типом редактируемой сущности. При этом методы getItem и initItem будут работать с конкретным типом сущности и прикладному коду не потребуется дополнительных приведений типов. Например: + public class CustomerEdit extends AbstractEditor<Customer> { +... + @Override + protected void initItem(Customer item) { + ... + Помимо общего для всех контроллеров метода init() в контроллере экрана редактирования можно переопределить следующие: + + + Методы инициализации: + + + initItem + + + postInit + + + + + Методы завершения: + + + postValidate + + + preCommit + + + postCommit + + + + + Диаграммы последовательностей +
+ Инициализация экрана + + + + + +
+
+ Коммит и закрытие экрана с фреймом editWindowActions + + + + + +
+
+ Коммит экрана с фреймом extendedEditWindowActions + + + + + +
+
+ Коммит и закрытие экрана с фреймом extendedEditWindowActions + + + + + +
+
+
+ Реализация для разных типов клиентов + Базовые классы контроллеров расположены в модуле gui базового проекта cuba и не содержат ссылок на классы реализации визуальных компонентов (Swing или Vaadin), что дает возможность использовать их в клиентах обоих типов. Вместо этого базовые классы контроллеров реализуют дополнительный интерфейс Window.Wrapper и делегируют выполнение "обернутому" окну. + В то же время конкретные классы контроллеров могут быть расположены как в модуле gui, так и в web или desktop, в зависимости от применяемых в проекте клиентских блоков и специфики экрана. Если контроллер является универсальным, но для разных типов клиента требуется дополнительная функциональность, ее можно определить в так называемых классах-компаньонах. + Класс-компаньон располагается в модуле клиента соответствующего типа (web или desktop) и реализует интерфейс, задаваемый в использующем его контроллере. Класс компаньона задается в элементе companions XML-дескриптора экрана. Контроллер может получить ссылку на экземпляр компаньона с помощью инжекции или вызовом getCompanion(), и в нужный момент передать ему управление, например для дополнительной инициализации визуальных компонентов специфичным для данного типа клиента способом. +
+
+
+
+ Библиотека визуальных компонентов + Компоненты + Контейнеры +
+ Компоненты +
+ Диаграмма компонентов + + + + + +
+ Component − предок всех визуальных компонентов. Он содержит базовые атрибуты, позволяющие идентифицировать компонент и располагать его на экране. + + + + + + + + Button + + + + + + + + + + + + PopupButton + + + + + + + + + + + + LinkButton + + + + + + + + + + + + Label + + + + + + + + + + + + TextField + + + + + + + + + + + + TextArea + + + + + + + + + + + + DateField + + + + + + + + + + + + TimeField + + + + + + + + + + + + CheckBox + + + + + + + + + + + + PickerField + + + + + + + + + + + + OptionsGroup + + + + + + + + + + + + LookupField + + + + + + + + + + + + LookupPickerField + + + + + + + + + + + + SearchPickerField + + + + + + + + + + + + TwinColumn + + + + + + + + + + + + FileUploadField + + + + + + + + + + + + Table + + + + + + + + + + + + TreeTable + + + + + + + + + + + + GroupTable + + + + + + + + + + + + Tree + + + + + + + + + + + + FieldGroup + + + + + + + + + + + + TokenList + + + + + + + + + + + + GenericFilter + + + + + + + + + + + + +
+ Label + Надпись (Label) − текстовый компонент, отображающий статический текст либо значение атрибута сущности. + XML-имя компонента: label +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_label_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент <code>Label</code> реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <figure> + <title>Типы надписей + + + + + +
+ Для того чтобы создать компонент надписи, создайте в xml-дескрипторе следующий код: + + Атрибут value предназначен для отображения текста надписи. Обычно значение атрибута задается с помощью ключа пакета сообщений. + Текст, содержащийся в атрибуте value, будет перенесен на следующую строку, если по длине он превысит значение атрибута width. Поэтому если Вам нужно использовать многострочную надпись, укажите требуемое значение атрибута width (применимо только для веб-приложений). Если текст надписи слишком длинный, и значение атрибута width не определено, то текст будет урезан. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="142" contentdepth="24" align="center" fileref="img/gui_label_truncated.png"/> + </imageobject> + </mediaobject> + </figure> + <para>В компоненте надписи есть возможность отображать значение атрибута сущности. Для этого используются атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/label/labelWithDatasource.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибуты <sgmltag>label</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row> + <entry align="left"> + <link linkend="attr_align">align</link> + </entry> + <entry align="left"> + <link linkend="attr_property">property</link> + </entry> + <entry align="left"> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_stylename">stylename</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry align="left"> + <link linkend="attr_value">value</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry align="left"> + <link linkend="attr_visible">visible</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>label</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_formatter">formatter</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_Button"> + <title>Button + Кнопка (Button) − компонент, обеспечивающий выполнение некоторых действий при нажатии на нее пользователем. + XML-имя компонента: button +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_Button_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент кнопки реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Кнопка может содержать текст или пиктограмму (или и то и другое). На рисунке ниже отражены разные виды кнопок.</para> + <figure> + <title>Типы кнопок + + + + + +
+ Для создания кнопки с текстом достаточно написать в xml-дескрипторе следующий код: + + Для создания кнопки с пиктограммой: + + Атрибут icon указывает на местоположение пиктограммы. Подробную информацию о том, где следует располагать файлы пиктограмм, Вы можете прочитать в + Для создания кнопки с пользовательским стилем необходимо указать атрибут stylename, а также задать стиль компонента в файле CSS. + + Определение стиля: + + Подробную информацию о том, как создавать пользовательский стиль, Вы можете прочитать в + Основная функция кнопки − выполнить некоторое действие при нажатии на нее. Определить метод контроллера, который будет вызываться при нажатии на кнопку, можно с помощью атрибута invoke. Значением атрибута должно быть имя метода контроллера, вызываемого в ответ на событие нажатия кнопки. Метод должен: + + + Быть public + + + Возвращать void + + + Не иметь аргументов или иметь один аргумент типа Component. Если метод имеет аргумент Component, то при вызове в него будет передан экземпляр вызвавшей кнопки. + + + В качестве примера показано описание кнопки, вызывающей метод lockButton: + + В контроллере экрана необходимо определить метод lockButton: + + Атрибут invoke игнорируется, если для кнопки задан атрибут action. Атрибут action содержит имя Action, соответствующего данной кнопке. + Пример кнопки с атрибутом action: + + Action для кнопки можно создавать программно, в контроллере экрана. + В качестве примера создадим кнопку, имеющую в xml-дескрипторе только атрибут id: + + В контроллере в методе init() экрана определим действие и название для кнопки. + + Атрибуты button: + + c + + + action + + + enable + + + stylename + + + + + align + + + icon + + + visible + + + + + caption + + + id + + + width + + + + + description + + + invoke + + + + +
+
+ PopupButton + Кнопка с выпадающим списком действий. + XML-имя компонента: popupButton. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_popupButton_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Кнопка с выпадающим списком действий может содержать текст или пиктограмму (или и то и другое). На рисунке ниже отражены разные виды кнопок.</para> + <figure> + <title>Типы кнопок + + + + + +
+ Для создания кнопки с текстом достаточно написать в xml-дескрипторе следующий код: + + Для создания кнопки с пиктограммой: + + Кнопка popupButton содержит выпадающий список действий, которые задаются в элементе actions. +
+ Кнопка с выпадающим списком действий + + + + + +
+ Атрибуты popupButton: + + c + + + align + + + icon + + + width + + + + + caption + + + id + + + + + description + + + stylename + + + + + enable + + + visible + + + + + Элементы popupButton: + + + + + + + actions + + + + + +
+
+ LinkButton + Кнопка-ссылка (LinkButton) − кнопка, выглядящая как гиперссылка. + XML-имя компонента: linkButton +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="70%" align="center" fileref="img/gui_linkButton_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент кнопки-ссылки реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Кнопка-ссылка может содержать текст или пиктограмму (или и то и другое). На рисунке ниже отражены разные виды кнопок.</para> + <figure> + <title>Типы кнопок-ссылок + + + + + +
+ Для создания кнопки с текстом достаточно написать в xml-дескрипторе следующий код: + + Для создания кнопки с пиктограммой: + + Для кнопки-ссылки, также как и для обычной кнопки (Button), можно в атрибуте invoke задать метод контроллера, который будет вызываться при нажатии на нее. + В качестве примера показано описание кнопки, вызывающей метод saveActionTest: + + В контроллере экрана необходимо определить метод saveActionTest: + + Атрибуты linkButton: + + c + + + action + + + enable + + + stylename + + + + + align + + + icon + + + visible + + + + + caption + + + id + + + width + + + + + description + + + invoke + + + + +
+
+ TextField + Поле для редактирования текста. + XML-имя компонента: textField +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_textField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент текстового поля реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Текстовое поле служит для обеспечения возможности получить текстовую информацию от пользователя, а также для отображения атрибутов <glossterm linkend="entity">сущности</glossterm>.</para> + <para>Для создания простого текстового поля достаточно в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> написать следующий код:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>На рисунке ниже показан вид простого текстового поля.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="180" align="center" fileref="img/gui_textField.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Для создания текстового поля, связанного с данными, необходимо использовать атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldDatasource.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="350" align="center" fileref="img/gui_textField_data.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Если поле не связано с источником данных (то есть не указан источник данных и название атрибута), можно связать текстовое поле с типом данных с помощью атрибута <property id="attr_datatype_1">datatype</property>. Атрибут используется для форматирования значения поля. Значением атрибута <property>datatype</property> является имя типа данных из списка:</para> + <itemizedlist> + <listitem> + <para><literal>boolean</literal></para> + </listitem> + <listitem> + <para><literal>byte array</literal></para> + </listitem> + <listitem> + <para><literal>date</literal></para> + </listitem> + <listitem> + <para><literal>decimal</literal></para> + </listitem> + <listitem> + <para><literal>double</literal></para> + </listitem> + <listitem> + <para><literal>int</literal></para> + </listitem> + <listitem> + <para><literal>long</literal></para> + </listitem> + <listitem> + <para><literal>string</literal></para> + </listitem> + <listitem> + <para><literal>uuid</literal></para> + </listitem> + </itemizedlist> + <para>В качестве примера рассмотрим текстовое поле, связанное с типом данных <literal>Integer</literal>. </para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldInt.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>При переходе из текстового поля, в котором введено значение, не являющееся типом данных <literal>Integer</literal>, приложение выдаст сообщение об ошибке и очистит текстовое поле.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="452" contentdepth="133" align="center" fileref="img/gui_textField_int.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Текстовому полю можно задавать количество строк и столбцов текста с помощью атрибутов <sgmltag id="attr_cols">cols</sgmltag> и <sgmltag id="attr_rows">rows</sgmltag>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldColsRows.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="450" contentdepth="96" align="center" fileref="img/gui_textFieldColsRows.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Вы можете связать текстовое поле с типом данных с помощью атрибута <sgmltag id="attr_datatype">datatype</sgmltag>. Атрибут используется для форматирования значения поля в том случае, если поле не связано с данными (то есть не указан источник данных и название атрибута). Значением атрибута <sgmltag>datatype</sgmltag> является имя типа данных из списка:</para> + <para>Для текстового компонента можно ограничить максимальную длину вводимого текста с помощью атрибута <sgmltag id="attr_maxLength">maxLength</sgmltag>. Значение атрибута устанавливается по умолчанию на основании длины строкового атрибута <glossterm linkend="entity">сущности</glossterm>, если этот параметр указан в аннотации сущности. Значение "-1" означает отсутствие ограничения.</para> + <para>Ниже показан пример, ограничивающий длину вводимого текста в 10 символов.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldMaxLength.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Для компонента <code>TextField</code> существует атрибут <sgmltag id="attr_resizable">resizable</sgmltag>. При задании атрибуту значения <literal>true</literal> и установке количества строк, больших одной, у пользователя появляется возможность изменять размеры компонента.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldResizable.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="330" align="center" fileref="img/gui_textField_resizable.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Для отсечения лишних пробелов в начале и конце значения используется атрибут <sgmltag id="attr_trim">trim</sgmltag>. По умолчанию значение атрибута равно <literal>true</literal>. Пробелы отсекаются в момент получения значения поля с помощью метода доступа.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldTrim.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>В <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана определяем метод, который будет вызываться при нажатии на <link linkend="gui_Button">кнопку</link>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldTrimContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибут <sgmltag id="attr_secret">secret</sgmltag> позволяет преобразовать текстовое поле в поле, которое не показывает символы, введенные пользователем. Вместо этого поле отображает эхо-символы, отличные от введенных. </para> + <para>Текстовое поле с атрибутом <code>secret="true"</code> скрывает введенные данные только в визуальном представлении. При получении значения поля с помощью метода доступа это значение представляется в текстовой форме.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldSecret.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>В <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана определяем метод, который будет вызываться при нажатии на <link linkend="gui_Button">кнопку</link>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/textField/textFieldSecretContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_textField_secret.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>textField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_editable">editable</link> + </entry><entry align="left"> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_secret">secret</link> + </entry></row> + <row> + <entry> + <link linkend="attr_cols">cols</link> + </entry> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry> + <link linkend="attr_resizable">resizable</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry> + <link linkend="attr_trim">trim</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_datatype">datatype</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_description">description</link> + </entry> + <entry> + <link linkend="attr_maxLength">maxLength</link> + </entry> + <entry> + <link linkend="attr_rows">rows</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>textField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_formatter">formatter</link> + </entry> + </row> + <row> + <entry> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_TextArea"> + <title>TextArea + Текстовая область (TextArea) − многострочное текстовое поле для редактирования текста. Содержит элементы управления для форматирования текста. + XML-имя компонента: textArea +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="180" align="center" fileref="img/gui_textArea_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент текстовой области реализован только для блока <structname>Web Client</structname>.</para> + <para>К тексту, вводимому в компоненте <code>TextArea</code>, можно применять средства для форматирования: изменять начертание шрифта, его размер, гарнитуру − с помощью элементов управления, расположенных в верхней части компонента.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="473" align="center" fileref="img/gui_textAreaInfo.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>textArea</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_editable">editable</link> + </entry><entry align="left"> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row> + <entry> + <link linkend="attr_cols">cols</link> + </entry> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_rows">rows</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>textArea</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_formatter">formatter</link> + </entry> + </row> + <row> + <entry> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_DateField"> + <title>DateField + Поле для отображения или ввода даты и времени. В самом простом варианте представляет собой поле для отображения даты, справа от него находится кнопка с выпадающим календарем, правее находится поле для ввода времени. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_dateFieldSimple.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Дата и время по умолчанию отображаются в формате, соответствующем выбранной локали.</para> + <para>XML-имя компонента: <sgmltag>dateField</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_dateField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Для создания поля даты, связанного с <link linkend="datasources">источником данных</link>, необходимо определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующим образом:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/dateField/dateField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Возможно изменить формат представления даты и времени с помощью атрибута <sgmltag id="attr_dateFormat">dateFormat</sgmltag> по правилам SimpleDateFormat (<ulink url="http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html">http://docs.oracle.com/javase/6/docs/ api/java/text/SimpleDateFormat.html</ulink>). Значением атрибута может быть либо строка формата, либо ключ в пакете сообщений (если строка начинается с msg://)</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/dateField/dateFieldFormat.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="210" align="center" fileref="img/gui_dateField_format.png"/> + </imageobject> + </mediaobject> + </figure> + <para>В большинстве бизнес-приложений нет необходимости отображать милисекунды или даже время. Точность представления даты или времени контролируется атрибутом <sgmltag id="attr_resolution">resolution</sgmltag>. Значение атрибута должно соответствовать перечислению <code>DateField.Resolution</code> − <literal>SEC</literal>, <literal>MIN</literal>, <literal>HOUR</literal>, <literal>DAY</literal>, <literal>MONTH</literal>, <literal>YEAR</literal>. По умолчанию точность определяется по аннотации <code>javax.persistence.Temporal</code> соответствующего атрибута <glossterm linkend="entity">сущности</glossterm>.</para> + <para>Если <code>resolution="DAY"</code> и не указан атрибут <sgmltag>dateFormat</sgmltag>, то в качестве формата будет взят формат, указанный в <link linkend="main_message_pack">главном пакете сообщений</link> с ключом <sgmltag>dateFormat</sgmltag>.</para> + <para>Если <code>resolution="MIN"</code> и не указан атрибут <sgmltag>dateFormat</sgmltag>, то в качестве формата будет взят формат, указанный в <link linkend="main_message_pack">главном пакете сообщений</link> с ключом <sgmltag>dateTimeFormat</sgmltag>.</para> + <para>Ниже показано определения поля для ввода даты с точностью до месяца.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/dateField/dateFieldResolution.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="350" align="center" fileref="img/gui_dateField_resolution.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>dateField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_editable">editable</link> + </entry><entry align="left"> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row> + <entry> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_dateFormat">dateFormat</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_resolution">resolution</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>dateField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_TimeField"> + <title>TimeField + Поле для отображения или ввода времени. В самом простом варианте представляет собой поле для отображения или ввода времени в часах и минутах. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="142" align="center" fileref="img/gui_timeField.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>timeField</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_timeField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Для создания поля времени, связанного с <link linkend="datasources">источником данных</link>, необходимо указать атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link> в определении поля:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/timeField/timeFieldDs.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Для создания простого поля для ввода времени необходимо определить его в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующим образом:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/timeField/timeField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>С помощью установки атрибута <sgmltag id="attr_showSeconds">showSeconds</sgmltag> в значение <literal>true</literal> можно настроить отображение секунд.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/timeField/timeFieldSec.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="235" align="center" fileref="img/gui_timeFieldSec.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>timeField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_enable">enable</link> + </entry><entry align="left"> + <link linkend="attr_required">required</link> + </entry><entry> + <link linkend="attr_visible">visible</link> + </entry></row> + <row> + <entry> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_showSeconds">showSeconds</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_editable">editable</link> + </entry> + <entry align="left"> + <link linkend="attr_property">property</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>timeField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_FieldGroup"> + <title>FieldGroup + Группа полей (FieldGroup) генерализует представление атрибутов одного или более источника данных. Представление поля зависит от типа соответствующего атрибута. Для отображения сущностей может использоваться как LookupField, так и PickerField. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_fieldGroup.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>fieldGroup</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_FieldGroup_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Ниже представлено описание группы полей в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> экрана:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/fieldGroup/fieldGroup.txt" encoding="UTF-8" parse="text"/></programlisting> + <para><link linkend="datasources">Источник данных</link> (атрибут <link linkend="attr_datasource" id="attr_fG_datasource">datasource</link>) можно задать как для всего компонента целиком, так и для каждого поля в отдельности. Если для компонента <sgmltag>fieldGroup</sgmltag> атрибут не задан, то атрибут <sgmltag>datasource</sgmltag> должен быть задан для каждого <link linkend="attr_field">поля</link> в отдельности, либо представление полей должно задаваться разработчиком.</para> + <para>При установке атрибута <sgmltag id="attr_collapsable">collapsable</sgmltag> в значение <literal>true</literal> компонент может сворачивать свое содержимое.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_fieldGroup_collapsable.png"/> + </imageobject> + </mediaobject> + </figure> + <para id="attr_captionAlignment"><sgmltag>captionAlignment</sgmltag> − необязательный атрибут, задает позицию отображения заголовков полей. Может принимать два значения: <literal>LEFT</literal>, <literal>TOP</literal>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_FieldGroup_captionAlignment.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент <sgmltag>fieldGroup</sgmltag> состоит из списков <link linkend="attr_field">полей</link>, сгруппированных в элементе <sgmltag id="attr_fG_column">column</sgmltag>. </para> + <itemizedlist> + <listitem> + <para>С помощью атрибута <sgmltag id="attr_fG_width">width</sgmltag> задается ширину всех полей (без учета заголовка) в колонке. Это значение может быть перекрыто в каждом отдельном поле.</para> + </listitem> + <listitem> + <para>Атрибут <sgmltag id="attr_fG_flex">flex</sgmltag> определяет соотношение ширин колонок.</para> + </listitem> + </itemizedlist> + <para>Элементами <link linkend="attr_fG_column">column</link> служат поля (<property>field</property>).</para> + <para id="attr_field"><sgmltag>field</sgmltag> − элемент, содержащий описание выводимого поля. Может содержать описание формата выводимого значения (<link linkend="element_formatter">formatter</link>) и описание валидаторов, применяемых к соответствующему полю (<link linkend="element_validator">validator</link>). Рассмотрим более подробно некоторые атрибуты элемента <sgmltag>field</sgmltag>. </para> + <itemizedlist> + <listitem> + <para><link linkend="attr_id" id="attr_field_id">id</link> − обязательный атрибут, идентификатор поля. Имеет физический смысл: в случае, если задан <link linkend="datasources">источник данных</link>, то идентификатором поля должно быть имя атрибута, иначе − просто идентификатор поля.</para> + </listitem> + <listitem> + <para><link linkend="attr_caption" id="attr_field_caption">caption</link> − необязательный атрибут, содержащий заголовок поля.</para> + </listitem> + <listitem> + <para><link linkend="attr_visible" id="attr_field_visible">visible</link> − необязательный атрибут, отвечает за сокрытие поля.</para> + </listitem> + <listitem> + <para><link linkend="attr_optionsDatasource" id="attr_field_optionsDatasource">optionsDatasource</link> − необязательный атрибут, определяющий список возможных значений поля.</para> + </listitem> + <listitem> + <para><link linkend="attr_datasource" id="attr_field_datasource">datasource</link> − необязательный атрибут, содержит имя <link linkend="datasources">источника данных</link> для конкретного поля.</para> + </listitem> + <listitem> + <para>Есть возможность самостоятельно задать представление поля с помощью атрибута <sgmltag id="attr_custom">custom</sgmltag>. Значение атрибута в этом случае должно быть равно <literal>true</literal>. Представление пользовательского поля необходимо определить в контроллере экрана. Компонент представления должен реализовывать интерфейс <code>Field</code>. Ниже представлен пример представления пользовательского поля:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/fieldGroup/fieldGroupCustom.txt" encoding="UTF-8" parse="text"/></programlisting> + </listitem> + <listitem> + <para><link linkend="attr_width" id="attr_field_width">width</link> − необязательный атрибут, содержит ширину поля (без учета заголовка)</para> + </listitem> + <listitem> + <para>Если значение атрибута <sgmltag id="attr_field_rows">rows</sgmltag> больше <literal>1</literal>, то в ячейке будет компонент <link linkend="gui_TextArea">TextArea</link>.</para> + </listitem> + </itemizedlist> + <para>Атрибуты <sgmltag>fieldGroup</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left">border</entry>editable<entry align="left"> + <link linkend="attr_fG_datasource">datasource</link> + </entry><entry align="left"> + <link linkend="attr_id">id</link> + </entry><entry/></row> + <row> + <entry> + <link linkend="attr_caption">caption</link> + </entry> + <entry align="left"> + <link linkend="attr_editable">editable</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_captionAlignment">captionAlignment</link> + </entry> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_collapsable">collapsable</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>fieldGroup</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry> + <link linkend="element_fG_field">field</link> + </entry> + </row> + <row> + <entry> + <link linkend="element_fG_column">column</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + <para>Атрибуты <link linkend="attr_field" id="element_fG_field">field</link>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_field_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_dateFormat">dateFormat</link> + </entry><entry align="left"> + <link linkend="attr_field_id">id</link> + </entry><entry> + <link linkend="attr_field_rows">rows</link> + </entry></row> + <row> + <entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry> + <link linkend="attr_maxLength">maxLength</link> + </entry> + <entry> + <link linkend="attr_showSeconds">showSeconds</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_clickAction">clickAction</link> + </entry> + <entry align="left"> + <sgmltag>descriptionProperty</sgmltag> + </entry> + <entry> + <link linkend="attr_field_optionsDatasource">optionsDatasource</link> + </entry> + <entry> + <link linkend="attr_field_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_cols">cols</link> + </entry> + <entry align="left"> + <link linkend="attr_editable">editable</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry> + <link linkend="attr_field_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_custom">custom</link> + </entry> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_field_datasource">datasource</link> + </entry> + <entry align="left">field</entry> + <entry> + <link linkend="attr_resolution">resolution</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты <link linkend="attr_fG_column" id="element_fG_column">column</link>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_fG_flex">flex</link> + </entry>editable</row> + <row> + <entry> + <link linkend="attr_fG_width">width</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_CheckBox"> + <title>CheckBox + Флажок (CheckBox) − компонент, имеющий два состояния: выбран, не выбран. Флажки используются +там, где нужно предоставить пользователю возможность что-то включить или +выключить. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="170" align="center" fileref="img/CheckBox.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>checkBox</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="70%" align="center" fileref="img/gui_checkBox_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Для создания флажка достаточно в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> написать следующий код:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/checkBox/checkBox.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Сброс или установка флажка изменяет его состояние. Состояние − это свойство типа <code>Boolean</code>, может быть получено с помощью метода <code>getValue()</code> и установлено с помощью метода <code>setValue()</code>. </para> + <para>При каждом изменении состояния флажка генерируется событие элемента, которое может быть обработано с помощью <code>ValueListener</code>.</para> + <para>Ниже приведен код <glossterm linkend="screen_controller_glossentry">контроллера</glossterm>, демонстрирующий работу с флажком. При установке флажка на экране возникает сообщение <quote>Разрешить создание нового документа</quote>, при сбросе флажка − <quote>Не разрешать создание нового документа</quote>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/checkBox/checkBoxContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибуты <sgmltag>checkBox</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_align">align</link> + </entry>editable<entry align="left"> + <link linkend="attr_editable">editable</link> + </entry><entry align="left"> + <link linkend="attr_property">property</link> + </entry><entry/></row> + <row> + <entry> + <link linkend="attr_caption">caption</link> + </entry> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>checkBox</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_PickerField"> + <title>PickerField + Поле ввода с дополнительными кнопками действий (PickerField) позволяет отображать экземпляр сущности в текстовом поле и выполнять действия нажатием на кнопки справа. + XML-имя компонента: pickerField. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_pickerField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Для того чтобы создать компонент <code>PickerField</code>, создайте в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующий код:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldMetaClass.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибут <sgmltag id="attr_metaclass">metaClass</sgmltag> задает тип <glossterm linkend="entity">сущности</glossterm>, экземпляр которой будет выбираться с помощью действия поиска (<literal>lookup</literal>). Для заданной сущности должен быть определен экран <literal>Название_сущности.lookup</literal> либо в атрибуте <sgmltag>lookupScreen</sgmltag> указан идентификатор экрана, который нужно открыть для выбора экземпляра сущности.</para> + <para>Определить компонент можно также следующим образом:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldProperty.txt" encoding="UTF-8" parse="text"/></programlisting> + <warning> + <para>Для правильной работы компонента <code>PickerField</code> необходима установка атрибута <link linkend="attr_metaclass">metaClass</link> либо одновременная установка атрибутов <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>.</para> + </warning> + <caution> + <title>Подсказка + Если при объявлении компонента никаких действий не задано, загрузчик XML определит для него действия lookup и clear. + + Поэтому если требуются все стандартные действия, их нужно определить с помощью элемента actions. actions − необязательный элемент, определяющий набор действий (action) (кнопок справа). В элементе action можно описывать как стандартные действия, так и произвольные. + Стандартные действия определяются перечислением PickerField.ActionType: lookup, clear, open. + +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="326" align="center" fileref="img/gui_pickerFieldActionsSt.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Ниже показано описание <code>PickerField</code>, имеющего произвольные действия.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldUserActions.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Действия необходимо определить в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/pickerField/pickerFieldUserActionsContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="286" align="center" fileref="img/gui_pickerField_custom.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Для компонента определены горячие клавиши. Для стандартных действий:</para> + <itemizedlist> + <listitem> + <para><code>Lookup</code> − <keycombo> + <keycap>CTRL</keycap> + <keycap>ALT</keycap> + <keycap>L</keycap> + </keycombo></para> + </listitem> + <listitem> + <para><code>Open</code> − <keycombo> + <keycap>CTRL</keycap> + <keycap>ALT</keycap> + <keycap>O</keycap> + </keycombo></para> + </listitem> + <listitem> + <para><code>Clear</code> − <keycombo> + <keycap>CTRL</keycap> + <keycap>ALT</keycap> + <keycap>C</keycap> + </keycombo></para> + </listitem> + </itemizedlist> + <para>Чтобы добавить пользовательскую горячую клавишу, необходимо чтобы класс <link linkend="gui_Action">действия</link> реализовывал интерфейс <code>com.haulmont.cuba.gui.components.ShortcutAction</code></para> + <para>Также поддерживается вызов действий сочетанием <keycombo> + <keycap>CTRL</keycap> + <keycap>ALT</keycap> + <keycap>1</keycap> + </keycombo>, <keycombo> + <keycap>CTRL</keycap> + <keycap>ALT</keycap> + <keycap>2</keycap> + </keycombo> и так далее.</para> + <para>Сочетания клавиш можно переназначить в ClientConfig</para> + <itemizedlist> + <listitem> + <para><property>cuba.gui.pickerShortcut.modifiers</property> − модификаторы для вызова <link linkend="gui_Action">действий</link> по порядковому номеру</para> + </listitem> + <listitem> + <para><property>cuba.gui.pickerShortcut.lookup</property> − Lookup Action</para> + </listitem> + <listitem> + <para><property>cuba.gui.pickerShortcut.open</property> − Open Action</para> + </listitem> + <listitem> + <para><property>cuba.gui.pickerShortcut.clear</property> − Clear Action</para> + </listitem> + </itemizedlist> + <para>Атрибуты <sgmltag>pickerField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_enable">enable</link> + </entry><entry align="left"> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_width">width</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_height">height</link> + </entry>required<entry> + <link linkend="attr_required">required</link> + </entry><entry/></row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_lookupScreen">lookupScreen</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry> + <link linkend="attr_metaclass">metaClass</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_OptionsGroup"> + <title>OptionsGroup + Компонент, который обеспечивает выбор из альтернатив, используя группу переключателей для выбора единственного значения из списка или используя группу флажков для выбора нескольких значений из списка. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="265" align="center" fileref="img/gui_optionsGroup.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>optionsGroup</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_OptionsGroup_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Если в качестве значений списка используются экземпляры <glossterm linkend="entity">сущностей</glossterm>, нужно использовать атрибут <link linkend="attr_optionsDatasource">optionsDatasource</link> при описании компонента.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/optionsGroup/optionsGroupOptDs.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Если в качестве значений списка используются, например, значения перечисления (enum), то следует использовать атрибуты <link linkend="attr_datasource">datasource</link> и <link linkend="attr_property">property</link>. </para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/optionsGroup/optionsGroupDatasource.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Значения списка можно создать программно, в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/optionsGroup/optionsGroup.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибут <sgmltag id="attr_orientation">orientation</sgmltag> задает расположение элементов группы. По умолчанию элементы располагаются по вертикали. Значение <literal>"horizontal"</literal> служит для горизонтального расположения.</para> + <para>Для задания режима выбора значений служит атрибут <sgmltag id="attr_multiselect">multiselect</sgmltag>. Если <code>"multiselect=false"</code>, то компонент будет представлен как группа переключателей, иначе − как группа флажков. По умолчанию значение атрибута равно <literal>false</literal>.</para> + <para>Атрибуты <sgmltag>optionsGroup</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_enable">enable</link> + </entry><entry align="left"> + <link linkend="attr_orientation">orientation</link> + </entry><entry> + <link linkend="attr_visible">visible</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_height">height</link> + </entry>required<entry> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_width">width</link> + </entry></row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_multiselect">multiselect</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>optionsGroup</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_LookupField"> + <title>LookupField + Компонент, реализующий раскрывающийся список. Раскрывающийся список служит для выбора одного из множества +доступных вариантов. В составе такого списка присутствует активизирующая его кнопка и поле редактирования. Раскрывающийся список реализует фильтрацию значений в реальном времени (по мере ввода значения пользователем) и постраничный вывод доступных значений. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="165" align="center" fileref="img/gui_lookupField.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>lookupField</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_LookupField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Если в качестве значений списка используются экземпляры <glossterm linkend="entity">сущностей</glossterm>, нужно использовать атрибут <link linkend="attr_optionsDatasource">optionsDatasource</link> при описании компонента.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/lookupField/lookupFieldOptDs.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Ниже представлено описание раскрывающегося списка, значения которого заданы с помощью установки <link linkend="datasources">источника данных</link> и имени атрибута <glossterm linkend="entity">сущности</glossterm>. Атрибут сущности является перечислением (enum).</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/lookupField/lookupField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>С помощью атрибута <sgmltag id="attr_filterMode">filterMode</sgmltag> можно задать тип фильтрации значений:</para> + <itemizedlist> + <listitem> + <para><literal>NO</literal> − нет фильтрации</para> + </listitem> + <listitem> + <para><literal>STARTS_WITH</literal> − по началу фразы</para> + </listitem> + <listitem> + <para><literal>CONTAINS</literal> − по любому вхождению</para> + </listitem> + </itemizedlist> + <para>Атрибуты <sgmltag>lookupField</sgmltag>: </para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_enable">enable</link> + </entry><entry align="left"> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry><entry> + <link linkend="attr_visible">visible</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_filterMode">filterMode</link> + </entry>required<entry> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_width">width</link> + </entry></row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry>nullName</entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>lookupField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_LookupPickerField"> + <title>LookupPickerField + Раскрывающийся список с расширенной функциональностью (LookupPickerField) позволяет отображать экземпляр сущности в текстовом поле, выбирать экземпляр в раскрывающемся списке и выполнять действия нажатием на кнопки справа. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="260" align="center" fileref="img/gui_lookupPickerField.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>lookupPickerField</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_LookupPickerField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Ниже представлено описание раскрывающегося списка с расширенной функциональностью.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/lookupPickerField/lookupPickerField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибуты <sgmltag>lookupPickerField</sgmltag>: </para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_enable">enable</link> + </entry><entry align="left">nullName</entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_filterMode">filterMode</link> + </entry>required<entry> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry><entry> + <link linkend="attr_visible">visible</link> + </entry></row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_property">property</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry/> + </row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry> + <link linkend="attr_metaclass">metaClass</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>lookupPickerField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_validator">validator</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_action">actions</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_SearchPickerField"> + <title>SearchPickerField + Поле поиска с расширенной функциональностью (SearchPickerField) служит для поиска значения по заранее определенному условию. Для работы с полем достаточно ввести хотя бы один символ и нажать клавишу Enter. Если будут найдены несколько совпадений, значения будут отображены в виде списка. С помощью дополнительных кнопок можно открывать экземпляр выбранной в поле сущности, удалять экземпляр или выбирать экземпляр из экрана списка сущностей. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="308" contentdepth="96" align="center" fileref="img/gui_searchPickerFieldOverlap.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>searchPickerField</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="90%" align="center" fileref="img/gui_SearchPickerField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован только для блока <structname>Web Client</structname>.</para> + <para>Для компонента <sgmltag>searchPickerField</sgmltag> необходимо предварительно создать <link linkend="datasources">источник данных</link>, предназначенный для работы с коллекцией <glossterm linkend="entity">сущностей</glossterm>, и задать в нем запрос, содержащий условия поиска. Например:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchPickerField/searchPickerField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>При описании компонента в <glossterm linkend="screen_xml_glossentry">XML-дескрипторе</glossterm> используйте атрибут <link linkend="attr_optionsDatasource">optionsDatasource</link>. </para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchPickerField/searchPickerField.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Значение атрибута <sgmltag id="minSearchStringLength_attr_1">minSearchStringLength</sgmltag> − это минимальное количество символов, необходимых для поиска значения.</para> + <para>В <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана можно указать содержание нотификаций, которые будут выводиться на экран в двух случаях:</para> + <itemizedlist> + <listitem> + <para>если количество введенных символов меньше значения атрибута <link linkend="minSearchStringLength_attr">minSearchStringLength</link></para> + </listitem> + <listitem> + <para>если не найдено совпадений существующих в источнике данных значений и введенных символов.</para> + </listitem> + </itemizedlist> + <para>Пример задания нотификаций в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchField/searchFieldContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Если при объявлении компонента никаких <link linkend="gui_Action">действий</link> не задано, загрузчик XML определит для него действия <literal>lookup</literal> и <literal>open</literal>.</para> + <para>Стандартные действия определяются перечислением <code>PickerField.ActionType</code>: <literal>lookup</literal>, <literal>clear</literal>, <literal>open</literal>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/searchPickerField/searchPickerFieldActions.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="257" contentdepth="97" align="center" fileref="img/gui_searchPickerFieldAllActions.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>searchPickerField</sgmltag>: </para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_enable">enable</link> + </entry><entry align="left"> + <link linkend="minSearchStringLength_attr">minSearchStringLength</link> + </entry><entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_filterMode">filterMode</link> + </entry>required<entry>nullName</entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_property">property</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry> + <link linkend="attr_metaclass">metaClass</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>searchPickerField</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry> + <link linkend="gui_Action">actions</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section id="gui_TwinColumn"> + <title>TwinColumn + Сдвоенный список (TwinColumn) представляет собой компонент множественного выбора, имеющий два находящихся рядом списка: доступных и выбранных опций. В левом списке содержатся доступные невыбранные значения, в правом списке содержатся выбранные значения. Пользователь может выбрать значения из левого списка и нажать на кнопку + + + + , переместив их таким образом в правый список. Значения могут быть отменены для выбора путем выделения их в правом списке и нажатия на кнопку + + + + . Для каждого значения можно задать уникальный стиль отображения и пиктограмму. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="235" align="center" fileref="img/TwinColumn.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>twinColumn</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="173" contentdepth="389" align="center" fileref="img/gui_TwinColumn_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован только для блока <structname>Web Client</structname>.</para> + <para>Ниже представлено описание сдвоенного списка в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/twinColumn/twinColumnOptDs.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>В данном примере используется атрибут <sgmltag id="attr_columns_twin">columns</sgmltag> для задания количества колонок текста в списке и атрибут <sgmltag id="attr_rows_twin">rows</sgmltag> для задания количества строк текста в списке.</para> + <para>Атрибуты <sgmltag>twinColumn</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_editable">editable</link> + </entry><entry align="left"> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_enable">enable</link> + </entry>required<entry> + <link linkend="attr_property">property</link> + </entry><entry> + <link linkend="attr_visible">visible</link> + </entry></row> + <row> + <entry align="left"> + <link linkend="attr_columns_twin">columns</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + <row> + <entry> + <link linkend="attr_description">description</link> + </entry> + <entry>nullName</entry> + <entry> + <link linkend="attr_rows">rows</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>twinColumn</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_validator">validator</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + <para>Для задания внешнего вида опций нужно реализовать интерфейс <code>com.haulmont.cuba.gui.components.StyleProvider</code> как показано в следующем примере:</para> + <programlisting>TwinColumn twinColumn = getComponent("groups"); + twinColumn.setStyleProvider(new TwinColumn.StyleProvider() { + public String getStyleName(Entity item, Object property, boolean selected) { + if (((Group) item).getName().equals("Company")) { + return "companyStyle"; + } else { + return null; + } + } + + public String getItemIcon(Entity item, boolean selected) { + if (((Group) item).getName().equals("Company")) { + return "theme:icons/company.png"; + } else { + return null; + } + } + });</programlisting> + </section> + <section id="gui_TokenList"> + <title>TokenList + Список маркеров (TokenList) представляет собой компонент, имеющий упрощенный вариант отображения и формирования списка сущностей. + Для выбора значений используется компонент, подобный LookupPickerField +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="310" align="center" fileref="img/gui_tokenList.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>tokenList</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_TokenList_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>.</para> + <para>Для создания списка маркеров необходимо задать описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tokenList/tokenList.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>С помощью атрибута <sgmltag id="attr_position">position</sgmltag> можно задавать позиционирование раскрывающегося списка. Атрибут может принимать два значения:</para> + <itemizedlist> + <listitem> + <para><literal>TOP</literal></para> + </listitem> + <listitem> + <para><literal>BOTTOM</literal></para> + </listitem> + </itemizedlist> + <figure> + <title>Список маркеров со значением атрибута position="BOTTOM" + + + + + +
+ Атрибут inline задает отображение списка выбранных значений: вертикально или горизонтально. Значение true соответствует горизонтальному расположению, значение false − вертикальному. +
+ Список маркеров с горизонтальным расположением выбранных значений + + + + + +
+ Установка атрибута simple в значение true позволяет сворачивать компонент, оставляя только кнопку добавления. При нажатии на кнопку добавления сразу показывается экран списка экземпляров сущности, для которой задан источник данных. +
+ Список маркеров со значением атрибута simple="true" + + + + + +
+ Элемент tokenList должен содержать следующие элементы: + + + lookup − описатель компонента выбора значений. + Атрибут lookup задает вид списка. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_tokenListLookup.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибут <sgmltag id="attr_lookupScreen">lookupScreen</sgmltag> задает идентификатор экрана для выбора значений в режиме <link linkend="attr_lookup">lookup</link>.</para> + <para>С помощью атрибута <sgmltag id="attr_openType">openType</sgmltag> можно задать способ открытия окна списка экземпляров сущности:</para> + <itemizedlist> + <listitem> + <para><literal>NEW_TAB</literal> − открытие экрана в новой вкладке</para> + </listitem> + <listitem> + <para><literal>DIALOG</literal> − открытие экрана в диалоговом окне</para> + </listitem> + <listitem> + <para><literal>NEW_WINDOW</literal> − открытие экрана в новом окне</para> + </listitem> + <listitem> + <para><literal>THIS_TAB</literal> − открытие экрана в текущей вкладке</para> + </listitem> + </itemizedlist> + </listitem> + <listitem> + <para id="element_button"><sgmltag>button</sgmltag> − описатель кнопки добавления значений</para> + </listitem> + </itemizedlist> + <para>Ниже расположен пример <glossterm linkend="screen_controller_glossentry">контроллера</glossterm>, в котором задаются обработчики добавления нового значения и удаления значения:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tokenList/tokenListContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибуты <sgmltag>tokenList</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_editable">editable</link> + </entry><entry align="left"> + <link linkend="attr_inline">inline</link> + </entry><entry> + <link linkend="attr_visible">visible</link> + </entry></row> + <row><entry> + <link linkend="attr_captionProperty">captionProperty</link> + </entry><entry align="left"> + <link linkend="attr_enable">enable</link> + </entry>required<entry> + <link linkend="attr_position">position</link> + </entry><entry> + <link linkend="attr_width">width</link> + </entry></row> + <row> + <entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_simple">simple</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>tokenList</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry align="left"> + <link linkend="element_tL_lookup">lookup</link> + </entry> + </row> + <row> + <entry> + <link linkend="element_tL_button">button</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + <para>Атрибуты <link linkend="element_lookup" id="element_tL_lookup">lookup</link>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_captionProperty">captionProperty</link> + </entry>editable<entry align="left"> + <link linkend="attr_lookupScreen">lookupScreen</link> + </entry><entry align="left"> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry><entry/></row> + <row><entry> + <link linkend="attr_filterMode">filterMode</link> + </entry><entry align="left"> + <sgmltag>multiselect</sgmltag> + </entry>required<entry/><entry/></row> + <row> + <entry align="left"> + <link linkend="attr_lookup">lookup</link> + </entry> + <entry align="left"> + <link linkend="attr_openType">openType</link> + </entry> + <entry/> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты <link linkend="element_button" id="element_tL_button">button</link>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable</row> + <row><entry> + <link linkend="attr_icon">icon</link> + </entry>required</row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_Table"> + <title>Table + Таблица (Table) дает возможность выводить двухмерную информацию, расположенную в виде строк и столбцов, настраивать и сортировать данные, управлять заголовками таблицы и ее выделенными элементами. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="300" align="center" fileref="img/gui_table.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>table</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="90%" align="center" fileref="img/gui_Table_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Интерфейс <code>Table</code>, наследуется от интерфейса <code>ListComponent</code> − базового интерфейса компонентов, предназначенных для работы с коллекциями <glossterm linkend="entity">сущностей</glossterm>.</para> + <para>Для того чтобы создать простую таблицу, необходимо в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> задать <link linkend="datasources">источник данных</link> и определить описание таблицы:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/table.txt" encoding="UTF-8" parse="text"/></programlisting> + <warning> + <para>Обязательными для таблицы являются элементы <link linkend="element_table_columns">columns</link> и <link linkend="element_table_rows">rows</link>.</para> + </warning> + <para>Атрибут <sgmltag id="attr_presentations">presentations</sgmltag> управляет механизмом <link linkend="gui_Table_presentations">представлений</link>. Значение по умолчанию равно <literal>false</literal>. Когда значение атрибута равно <literal>true</literal>, то в верхнем правом углу таблицы появляется значок <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_presentation.png"/> + </imageobject> + </inlinemediaobject>.</para> + <para>Атрибут <sgmltag id="attr_sortable">sortable</sgmltag> разрешает или запрещает сортировку в таблице. По умолчанию имеет значение <literal>true</literal>. Если сортировка разрешена, то при нажатии на название колонки справа от названия появляется значок <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_sortable_down.png"/> + </imageobject> + </inlinemediaobject>/<inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_sortable_up.png"/> + </imageobject> + </inlinemediaobject>.</para> + <para>Существует возможность использования для колонок функций <link linkend="gui_Table_aggregation">агрегирования</link> без модификации <link linkend="datasources">источника данных</link> с помощью атрибута <sgmltag id="attr_aggregatable">aggregatable</sgmltag>. По умолчанию атрибут имеет значение <literal>false</literal>.</para> + <para>Атрибут <sgmltag id="attr_showTotalAggregation">showTotalAggregation</sgmltag> разрешает или запрещает отображение строки итоговой агрегации в таблице. По умолчанию имеет значение <literal>true</literal>.</para> + <para>Можно задать множественное выделение строк в таблице с помощью присваивания атрибуту <sgmltag id="attr_table_multiselect">multiselect</sgmltag> значения <literal>true</literal>. По умолчанию значение атрибута равно <literal>false</literal>.</para> + <para>С помощью элемента <sgmltag id="element_table_actions">actions</sgmltag> возможно определять набор действий (<link linkend="gui_Action">action</link>). Кроме описания произвольного действия возможно использование стандартных действий, определяемых перечислением <code>ListActionType</code>: <code>create</code>, <code>edit</code>, <code>remove</code>, <code>refresh</code>, <code>add</code>, <code>exclude</code>, <code>excel</code>.</para> + <para><sgmltag id="attr_rowsCount">rowsCount</sgmltag> − необязательный элемент, создающий для таблицы компонент <code>RowsCount</code>, который позволяет загружать в таблицу данные постранично.</para> + <para>Элемент <sgmltag id="element_table_rows">rows</sgmltag> является обязательным. В этом элементе необходимо объявить используемый <link linkend="datasources">источник данных</link>.</para> + <para>Атрибут <sgmltag id="attr_headerMode">headerMode</sgmltag> элемента <sgmltag>rows</sgmltag> задает вариант отображения заголовков рядов:</para> + <itemizedlist> + <listitem> + <para><literal>NONE</literal> − нет заголовков</para> + </listitem> + <listitem> + <para><literal>ICON</literal> − пиктограмма</para> + </listitem> + </itemizedlist> + <para>Следующим обязательным элементом для таблицы является элемент <sgmltag id="element_table_columns">columns</sgmltag>. Он определяет набор колонок (<sgmltag>column</sgmltag>) таблицы.</para> + <para><sgmltag id="element_table_columns_column_1">column</sgmltag> − элемент, задающий опции для колонки таблицы. Может содержать следующие атрибуты:</para> + <itemizedlist> + <listitem> + <para><sgmltag id="attr_table_column_id">id</sgmltag> − обязательный атрибут, содержит название атрибута <glossterm linkend="entity">сущности</glossterm>, выводимого в колонке.</para> + </listitem> + <listitem> + <para><sgmltag id="attr_table_column_collapsed">collapsed</sgmltag> − необязательный атрибут, скрывает/показывает колонку. По умолчанию имеет значение <literal>false</literal>.</para> + </listitem> + <listitem> + <para><sgmltag id="attr_table_column_caption">caption</sgmltag> − необязательный атрибут, содержит заголовок колонки.</para> + </listitem> + <listitem> + <para><sgmltag id="attr_table_column_width">width</sgmltag> − необязательный атрибут, отвечает за изначальную ширину колонки.</para> + </listitem> + <listitem> + <para><sgmltag id="attr_table_column_calculatable">calculatable</sgmltag> − необязательный атрибут, используется только в редактируемой таблице. Буквально означает, что значения в данной колонке являются вычислимыми и зависят от значений других колонок.</para> + </listitem> + <listitem> + <para><link linkend="attr_editable" id="attr_table_column_editable">editable</link> − необязательный атрибут, разрешает/запрещает редактирование данного атрибута в редактируемой таблице.</para> + </listitem> + </itemizedlist> + <para>Для таблицы можно задать стиль отображения ячеек таблицы. Для этого в <glossterm linkend="screen_controller_glossentry">контроллере</glossterm> экрана нужно задать для таблицы <code>StyleProvider</code> реализовав интерфейс <code>com.haulmont.cuba.gui.components.StyleProvider.</code></para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/tableStyle.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Далее нужно определить в теме приложения стили для строк и столбцов (подробную информацию о том, как создать тему приложения, смотрите <xref linkend="gui_themes"/>):</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/tableStylecss.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="381" align="center" fileref="img/gui_table_Style.png"/> + </imageobject> + </mediaobject> + </figure> + <para>В таблице существует возможность задавать собственное представление данных в колонке. Для этого необходимо использовать метод <code>Table.addGeneratedColumn</code>. В методе нужно создать CUBA-компонент с использованием класса <code>ComponentsFactory</code>. Пример:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/table/tableGeneratedColumn.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="501" align="center" fileref="img/gui_tableGenerated.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>table</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_aggregatable">aggregatable</link> + </entry>editable<entry align="left">margin</entry><entry align="left"> + <link linkend="attr_sortable">sortable</link> + </entry><entry/></row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry align="left"> + <link linkend="attr_table_multiselect">multiselect</link> + </entry> + <entry> + <link linkend="attr_stylename">styleName</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry align="left"> + <link linkend="attr_presentations">presentations</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry align="left"> + <link linkend="attr_showTotalAggregation">showTotalAggregation</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>table</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"> + <colspec colname="c1"/> + <tbody> + <row> + <entry> + <link linkend="element_table_actions">actions</link> + </entry> + </row> + <row>buttonsPanel<entry> + <link linkend="gui_ButtonsPanel">buttonsPanel</link> + </entry></row> + <row> + <entry> + <link linkend="attr_rowsCount">rowsCount</link> + </entry> + </row> + <row> + <entry> + <link linkend="element_t_columns">columns</link> + </entry> + </row> + <row> + <entry> + <link linkend="element_t_rows">rows</link> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + <para>Атрибуты элемента <link linkend="element_table_columns" id="element_t_columns">columns</link> <link linkend="element_table_columns_column_1">column</link>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="gui_Table_aggregation">aggregation</link> + </entry>editable<entry align="left"> + <link linkend="attr_clickAction">clickAction</link> + </entry><entry align="left"> + <link linkend="attr_table_column_id">id</link> + </entry><entry> + <link linkend="attr_resolution">resolution</link> + </entry></row> + <row> + <entry> + <link linkend="attr_table_column_calculatable">calculatable</link> + </entry> + <entry align="left"> + <link linkend="attr_table_column_collapsed">collapsed</link> + </entry> + <entry> + <link linkend="attr_optionsDatasource">optionsDatasource</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_table_column_caption">caption</link> + </entry> + <entry align="left"> + <link linkend="attr_dateFormat">dateFormat</link> + </entry> + <entry> + <link linkend="attr_required">required</link> + </entry> + <entry> + <link linkend="attr_table_column_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_captionProperty">captionProperty</link> + </entry> + <entry align="left"> + <link linkend="attr_table_column_editable">editable</link> + </entry> + <entry> + <link linkend="attr_requiredMessage">requiredMessage</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты <link linkend="element_table_rows" id="element_t_rows">rows</link>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_datasource">datasource</link> + </entry>editable</row> + <row> + <entry> + <link linkend="attr_headerMode">headerMode</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_TreeTable"> + <title>TreeTable + Таблица с иерархией (TreeTable) − таблица, отображающая иерархию сущностей. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="321" align="center" fileref="img/gui_treeTable.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>treeTable</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="90%" align="center" fileref="img/gui_TreeTable_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Для того чтобы создать иерархическую таблицу, необходимо в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> задать иерархический <link linkend="datasources">источник данных</link> и определить описание таблицы:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/treeTable/treeTable.txt" encoding="UTF-8" parse="text"/></programlisting> + <warning> + <para>Обязательными для иерархической таблицы являются элементы <link linkend="element_table_columns">columns</link> и <link linkend="element_table_rows">rows</link>.</para> + </warning> + </section> + <section id="gui_GroupTable"> + <title>GroupTable + Таблица с возможностью динамической группировки по любому полю. Для того чтобы сгруппировать таблицу по какому-либо столбцу, нужно в шапке таблицы перетащить этот столбец перед знаком + + + + . Сгруппированные значения можно разворачивать и сворачивать с помощью знаков + + + + / + + + + . +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="357" align="center" fileref="img/gui_groupTableDragColumn.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>groupTable</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_GroupTable_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Для правильного функционирования <code>GroupTable</code> должна быть соединена с <link linkend="datasources">GroupDatasource</link>.</para> + <para>Пример использования:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/groupTable/groupTable.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Элемент <sgmltag>group</sgmltag> − необязательный элемент, может в единственном экземпляре находиться внутри <link linkend="element_table_columns">columns</link>. Содержит набор элементов <link linkend="element_table_columns_column_1">column</link>, по которым будет выполняться первоначальная группировка.</para> + </section> + <section id="gui_Tree"> + <title>Tree + Компонент Tree представляет иерархическую структуру данных, отображая ее в виде дерева. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="100" align="center" fileref="img/Tree.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>tree</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_tree_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Для того чтобы создать компонент дерева, создайте в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> следующий код:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tree/tree.txt" encoding="UTF-8" parse="text"/></programlisting> + <warning> + <para>Обязательными для дерева являются элемент <sgmltag>treechildren</sgmltag>.</para> + </warning> + <para>Элемент <sgmltag id="element_treechildren">treechildren</sgmltag> декларирует используемый <link linkend="datasources">источник данных</link>. </para> + <para>Атрибуты <sgmltag>tree</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_height">height</link> + </entry>editable</row> + <row><entry> + <link linkend="attr_id">id</link> + </entry>required<entry/></row> + <row> + <entry align="left"> + <link linkend="attr_table_multiselect">multiselect</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_width">width</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>tree</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"><colspec colname="c1"/>c <tbody> + <row><entry align="left"> + <link linkend="element_treechildren">treechildren</link> + </entry>editable</row> + <row><entry> + <link linkend="element_table_actions">actions</link> + </entry>required</row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты <sgmltag>treechildren</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_captionProperty">captionProperty</link> + </entry>editable</row> + <row><entry> + <link linkend="attr_datasource">datasource</link> + </entry>required</row> + <row> + <entry align="left"> + <sgmltag>hierarchyProperty</sgmltag> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_ProgressBar"> + <title>ProgressBar + Индикатор прогресса (ProgressBar) наглядно демонстрирует пользователю, на каком этапе выполнения находится некий процесс, так что пользователь сможет понять, как долго остается ждать его завершения. Индикатор выводится на +экран в виде непрерывно меняющейся полосы. Может отображать конкретное значение прогресса, например, 30%, а может отображать неопределенное состояние. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_progressBar.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>progressBar</sgmltag></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_progressBar_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Для создания индикатора необходимо в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm> определить его описание:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/progressBar/progressBar.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>С помощью атрибута <sgmltag id="attr_indeterminate">indeterminate</sgmltag> можно задать отображение неопределенного состояния индикатора. Если значение атрибута равно <literal>true</literal>, то индикатор отображает неопределенное состояние. Значение атрибута по умолчанию равно <literal>true</literal>.</para> + <para>Значения прогресса задачи для индикатора прогресса следует передавать в значениях типа <code>Float</code> в диапазоне от 0.0 до 1.0.</para> + <para>Атрибуты <sgmltag>progressBar</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_align">align</link> + </entry>editable<entry align="left"> + <link linkend="attr_height">height</link> + </entry><entry align="left"> + <link linkend="attr_stylename">stylename</link> + </entry><entry/></row> + <row> + <entry> + <link linkend="attr_editable">editable</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_enable">enable</link> + </entry> + <entry align="left"> + <link linkend="attr_indeterminate">indeterminate</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_FileUploadField"> + <title>FileUploadField + Компонент загрузки файлов (FileUploadField) позволяет пользователю загружать файлы на сервер. Компонент представляет собой кнопку, при нажатии на которую на экране отображается окно загрузки файла, содержащее список файлов некоторой директории. В списке можно выбрать только один файл для загрузки. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_upload.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>upload</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_FileUploadField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Ниже расположен пример, содержащий обработчик загрузки файла. В этом обработчике написан программный код, обеспечивающий вывод на экран сообщения об успешной или не успешной загрузке файла, а также вывод названия файла в компоненте <link linkend="gui_Label">надписи</link>.</para> + <para>Для начала определим описание компонента в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/upload/upload.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Программный код <glossterm linkend="screen_controller_glossentry">контроллера</glossterm> выглядит следующим образом:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/upload/uploadContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_uploadRez.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>upload</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_id">id</link> + </entry><entry align="left"> + <link linkend="attr_width">width</link> + </entry><entry/></row> + <row> + <entry> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry align="left"> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_FileMultiUploadField"> + <title>FileMultiUploadField + Компонент для множественной загрузки файлов позволяет пользователю загружать файлы на сервер. Компонент представляет собой кнопку, при нажатии на которую на экране отображается окно загрузки файлов, содержащее список файлов некоторой директории. В списке можно выбрать сразу несколько файлов для загрузки. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_multipleUpload.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>multiupload</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_FileMultiUploadField_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Компонент реализован для блоков <structname>Web Client</structname> и <structname>Desktop Client</structname>. </para> + <para>Ниже расположен пример, содержащий обработчик загрузки файлов. В этом обработчике написан программный код, обеспечивающий вывод на экран сообщения об успешной или не успешной загрузке файлов, а также вывод названий файлов в <link linkend="gui_TextField">текстовое поле</link>.</para> + <para>Для начала определим описание компонента в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/multipleUpload/multipleUpload.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Программный код <glossterm linkend="screen_controller_glossentry">контроллера</glossterm> выглядит следующим образом:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/multipleUpload/multipleUploadContr.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_multipleUploadRez.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>multiupload</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_id">id</link> + </entry><entry align="left"> + <link linkend="attr_width">width</link> + </entry><entry/></row> + <row> + <entry> + <link linkend="attr_description">description</link> + </entry> + <entry align="left"> + <link linkend="attr_stylename">stylename</link> + </entry> + <entry/> + <entry/> + </row> + <row> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry align="left"> + <link linkend="attr_visible">visible</link> + </entry> + <entry/> + <entry/> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_Filter"> + <title>Filter + Универсальный фильтр (Filter) − компонент для организации поиска данных, предназначен для отбора информации, отображаемой в таблице экземпляров сущностей. + Типичный фильтр имеет следующий вид: +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_descr.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Для того чтобы создать фильтр, нажмите на кнопку <guibutton>Фильтр</guibutton> и выберите значение <guilabel>Создать</guilabel>. На экране отобразится панель редактора фильтра.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_editor.png"/> + </imageobject> + </mediaobject> + </figure> + <para>В поле <guilabel>Имя</guilabel> следует ввести имя фильтра, это имя будет отображаться в списке доступных для текущего экрана фильтров.</para> + <para>В таблице содержатся условия фильтра. Условия можно менять местами с помощью кнопок <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_filter_cond_down.png"/> + </imageobject> + </inlinemediaobject>/<inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_filter_cond_up.png"/> + </imageobject> + </inlinemediaobject>. Созданные условия можно удалять с помощью кнопки <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_filter_remove.png"/> + </imageobject> + </inlinemediaobject></para> + <para>С помощью флажков можно сделать выбранное в таблице условие скрытым для пользователя (колонка <guilabel>Скрытый</guilabel>) или обязательным для заполнения (колонка <guilabel>Обязательный</guilabel>). В колонке <guilabel>Операция</guilabel> следует для <emphasis role="bold">каждого</emphasis> условия выбрать операцию из списка доступных операций.</para> + <para>Фильтр можно сделать <firstterm>глобальным</firstterm> (то есть доступным для всех пользователей) с помощью установки флажка <guilabel>Общий для всех пользователей</guilabel>, или установить текущий фильтр в качестве фильтра по умолчанию с помощью установки флажка <guilabel>По умолчанию</guilabel>.</para> + <para>Чтобы добавить новое условие, следует нажать кнопку <guibutton>Добавить условие</guibutton> в редакторе фильтра. На экране отобразится окно выбора условий. </para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_conditions.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Условия разделяются по группам условий. Группа <guilabel>Атрибуты</guilabel> − это те условия, которые перечислены в элементах <sgmltag>properties</sgmltag> и <sgmltag>property</sgmltag> компонента фильтра. Группа <guilabel>Специальные условия</guilabel> − это пользовательские условия, заданные в элементе <sgmltag>custom</sgmltag>. Группы <guilabel>Группы</guilabel>, <guilabel>Динамический атрибут...</guilabel> и <guilabel>Создать новое...</guilabel> доступны для выбора всегда.</para> + <para/> + <para>XML-имя компонента: <sgmltag>filter</sgmltag>.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_filter_dia.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Существует два класса компонентов фильтра: для веб-приложений и для десктоп-приложений.</para> + <para>Компонент фильтра имеет следующие атрибуты, перечисленные ниже.</para> + <para><sgmltag id="applyTo_attr">applyTo</sgmltag> − необязательный атрибут, содержит <link linkend="attr_id">идентификатор</link> компонента, с которым связан фильтр. Используется в случае, когда необходимо иметь доступ к <link linkend="gui_Table_presentations">представлениям</link> связного компонента. Например, сохраняя фильтр как <link linkend="search_folder">папку поиска</link> или как <link linkend="application_folder">папку приложения</link>, можно указать, какое представление будет применятся при просмотре этой папки.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_apply_to.png"/> + </imageobject> + </mediaobject> + </figure> + <para><sgmltag id="useMaxResults_attr">useMaxResults</sgmltag> − необязательный атрибут, при указании значения <literal>false</literal> фильтр не будет отображать флажок <guilabel>Показывать N строк</guilabel>. По умолчанию флажок отображается, если есть специфическое разрешение <property>cuba.gui.filter.maxResults</property> (подробнее о специфических разрешениях смотрите в <productname>Руководстве по безопасности платформы <trademark>CUBA</trademark></productname> (<ulink url="http://docs.haulmont.com/cuba/">http://docs.haulmont.com/cuba/</ulink>)). Если атрибут <sgmltag>useMaxResults</sgmltag> не указан или значение атрибута равно <literal>true</literal>, а разрешение <property>cuba.gui.filter.maxResults</property> запрещено, фильтр будет принудительно отбирать только первые N строк без возможности пользователя отключить это. Число N определяется параметрами <literal>FetchUI</literal>, <literal>DefaultFetchUI</literal>, задаваемыми в <code>PersistenceManager</code>.</para> + <para>На рисунке далее показан вид фильтра со значением атрибута <code>useMaxResults="true"</code>, запретом специфического разрешения <property>cuba.gui.filter.maxResults</property> и параметром <code>DefaultFetchUI=2</code></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_useMaxRezult.png"/> + </imageobject> + </mediaobject> + </figure> + <para><sgmltag id="manualApplyRequired_attr">manualApplyRequired</sgmltag> − необязательный атрибут. Определяет, как будет применяться фильтр. Если значение атрибута равно <literal>false</literal>, то фильтр будет применяться сразу при открытии экрана списка. Если значение атрибута равно true, то фильтр будет применяться только после нажатия на кнопку <guibutton>Применить</guibutton>. Данный атрибут имеет приоритет над общесистемным <link linkend="app_properties_glossentry">свойством</link> <property>cuba.gui.genericFilterManualApplyRequired</property>.</para> + <para>Если значение атрибута <sgmltag id="required_filter_attr">required</sgmltag> равно <literal>true</literal>, то в списке фильтров значение <literal><без фильтрации></literal> не отображается, и должен быть выбран один из доступных фильтров. Если для экрана не установлен фильтр по умолчанию, то в списке выбора фильтра автоматически устанавливается первый созданный фильтр.</para> + <para>Если значение атрибута <sgmltag id="editable_filter_attr">editable</sgmltag> равно <literal>false</literal>, то кнопка <guibutton>Фильтр</guibutton> скрывается.</para> + <para>Компонент <sgmltag>filter</sgmltag> может содержать следующие элементы:</para> + <variablelist> + <varlistentry> + <term> + <sgmltag>properties</sgmltag> + </term> + <listitem> + <para>Элемент, определяющий набор атрибутов <glossterm linkend="entity">сущности</glossterm>, заданной <link linkend="datasources">источником данных</link>. В качестве названий атрибутов используются <glossterm linkend="localization">локализованные имена атрибутов сущностей</glossterm>. Атрибуты:</para> + <itemizedlist> + <listitem> + <para><sgmltag id="include_filter_attr">include</sgmltag> − обязательный атрибут, содержит регулярное выражение для включения атрибутов сущности. Атрибуты-коллекции не включаются независимо от данного регулярного выражения.</para> + <para>Чтобы включить все атрибуты сущности, используйте значение атрибута <literal>.*</literal> </para> + </listitem> + <listitem> + <para><sgmltag id="exclude_filter_attr">exclude</sgmltag> − необязательный атрибут, содержит регулярное выражение для исключения атрибутов сущности из предварительно включенных с помощью <link linkend="include_filter_attr">include</link>.</para> + <para>Чтобы исключить несколько атрибутов, перечислите их в значении атрибута <link linkend="exclude_filter_attr">exclude</link>, разделяя символом <literal>|</literal></para> + </listitem> + </itemizedlist> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_properties.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_properties.png"/> + </imageobject> + </mediaobject> + </figure> + </listitem> + </varlistentry> + <varlistentry> + <term> + <sgmltag>property</sgmltag> + </term> + <listitem> + <para>Элемент, определяющий один атрибут сущности. Атрибуты:</para> + <itemizedlist> + <listitem> + <para><sgmltag id="name_filter_property_attr">name</sgmltag> − имя атрибута сущности. Может быть путем (через ".") по графу <glossterm linkend="entity">сущностей</glossterm>.</para> + <warning> + <para>Если в качестве имени атрибута указан путь по графу сущностей, обязательно указание значения для атрибута <link linkend="caption_filter_attr">caption</link>.</para> + </warning> + </listitem> + <listitem> + <para><sgmltag id="caption_filter_attr">caption</sgmltag> − атрибут для задания локализованного названия имени параметра</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_property_caption.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_property_caption.png"/> + </imageobject> + </mediaobject> + </figure> + </listitem> + <listitem> + <para><sgmltag id="paramWhere_filter_attr">paramWhere</sgmltag> − необязательный атрибут для задания фильтра на список значений параметра-<glossterm linkend="entity">сущности</glossterm>. </para> + <para>Например, можно ограничить список предлагаемых пользователю моделей автомобилей только моделями марки <literal>Audi</literal>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_paramWhere.txt" encoding="UTF-8" parse="text"/></programlisting> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_paramWhere.png"/> + </imageobject> + </mediaobject> + </figure> + </listitem> + <listitem> + <para><sgmltag id="paramView_filter_attr">paramView</sgmltag> − необязательный атрибут для задания <link linkend="views">представления</link>, с которым будут загружаться список значений параметра-<glossterm linkend="entity">сущности</glossterm>. Например, <literal>_local</literal>.</para> + </listitem> + </itemizedlist> + </listitem> + </varlistentry> + <varlistentry> + <term> + <sgmltag>custom</sgmltag> + </term> + <listitem> + <para>Элемент, определяющий произвольное условие. Содержимым элемента должно быть выражение на <glossterm linkend="jpql">JPQL</glossterm> (возможно использование JPQL Macros), которое будет добавлено в условие <code>where</code> результирующего запроса. Параметр (если он есть) обозначается символом <literal>?</literal>. </para> + <para>Пример фильтра с произвольными условиями:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_custom.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Созданные произвольные условия попадают в раздел <guilabel>Специальные условия</guilabel> в редакторе условий.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_custom.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты элемента <sgmltag>custom</sgmltag>:</para> + <itemizedlist> + <listitem> + <para><sgmltag id="name_filter_custom_attr">name</sgmltag> − имя условия</para> + </listitem> + <listitem> + <para><sgmltag id="caption_filter_custom_attr">caption</sgmltag> − <link linkend="localization">локализованное</link> название условия</para> + </listitem> + <listitem> + <para><sgmltag id="paramClass_filter_attr">paramClass</sgmltag> или <sgmltag>class</sgmltag> − класс параметра условия</para> + </listitem> + <listitem> + <para><sgmltag id="inExpr_filter_attr">inExpr</sgmltag> − значение должно быть равно <literal>true</literal>, если выражение <glossterm linkend="jpql">JPQL</glossterm> содержит условие <code>in (?)</code></para> + </listitem> + <listitem> + <para><sgmltag id="join_filter_attr">join</sgmltag> − необязательный атрибут для задания строки, которая будет добавлена в секцию <literal>join</literal> запроса.</para> + </listitem> + <listitem> + <para><sgmltag>paramWhere</sgmltag> − необязательный атрибут для задания фильтра на список значений параметра-сущности.</para> + </listitem> + <listitem> + <para><sgmltag>paramView</sgmltag> − необязательный атрибут для задания <link linkend="views">представления</link>, с которым будут загружаться список значений параметра-<glossterm linkend="entity">сущности</glossterm>. Например, <literal>_local</literal>.</para> + </listitem> + </itemizedlist> + </listitem> + </varlistentry> + </variablelist> + <para>Атрибут <sgmltag>paramWhere</sgmltag> − необязательный атрибут для задания фильтра на список значений параметра-сущности. Содержит условие в формате <glossterm linkend="jpql">JPQL</glossterm> без слова <literal>where</literal>. Алиас сущности можно задавать любым. + +Можно использовать параметры экрана, атрибуты сессии, а также <link linkend="component_filter_name_caution">компоненты</link> экрана, в том числе отображающие другие параметры.</para> + <caution id="component_filter_name_caution"> + <title>Подсказка + Как узнать имя компонента, отображающего параметр? + Для этого нужно в редакторе фильтра правой кнопкой мыши вызвать контекстное меню в строке таблицы условий и выбрать в нем значение Показать имя компонента. После этого на экране отобразится окно, содержащее имя компонента. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_component_name.png"/> + </imageobject> + </mediaobject> + </figure> + </caution> + <para><emphasis role="bold">Права пользователей</emphasis></para> + <itemizedlist> + <listitem> + <para>Для создания/изменения/удаления глобальных фильтров пользователь должен иметь разрешение <property>cuba.gui.filter.global</property> </para> + </listitem> + <listitem> + <para>Для создания/изменения новых условий пользователь должен иметь разрешение <property>cuba.gui.filter.customConditions</property></para> + </listitem> + <listitem> + <para>Для возможности изменять значение флажка <guilabel>Show first N rows</guilabel> пользователь должен иметь разрешение <property>cuba.gui.filter.maxResults</property></para> + </listitem> + </itemizedlist> + <para>Информацию о том, как настраивать специфические разрешения, смотрите в <productname>Руководстве по безопасности платформы <trademark>CUBA</trademark></productname> (<ulink url="http://docs.haulmont.com/cuba/">http://docs.haulmont.com/cuba/</ulink>)</para> + <para><emphasis role="bold">Общесистемные параметры</emphasis></para> + <para>В системе предусмотрены следующие <glossterm linkend="app_properties_glossentry">параметры</glossterm>, влияющие на поведение фильтров:</para> + <itemizedlist> + <listitem> + <para><property>cuba.gui.genericFilterManualApplyRequired</property> − означает, что фильтр будет применяться только после нажатия пользователем соответствующей кнопки <guibutton>Применить</guibutton>. Значение по умолчанию равно <literal>true</literal>. Если выставлено значение <literal>false</literal>, то фильтр будет применяться сразу при открытии экрана списка. При открытии экрана списка с помощью <link linkend="application_folder">папки приложения</link> или <link linkend="search_folder">папки поиска</link> значение <property>cuba.gui.genericFilterManualApplyRequired</property> не учитывается. По умолчанию в этом случае в экране поиска фильтр будет применяться. Фильтр не применится, если значение атрибута <code>applyDefault</code> у папки явно установлено в <literal>false</literal>.</para> + </listitem> + <listitem> + <para><property>cuba.gui.genericFilterChecking</property> − означает, что перед применением фильтра будет выполняться проверка его условий. Если фильтр не имеет ни одного заданного условия, то применяться он не будет. Значение по умолчанию равно <literal>true</literal>. Если значение равно <literal>false</literal>, то проверка не производится.</para> + </listitem> + <listitem> + <para><property>cuba.gui.genericFilterTreeConditionSelect</property> − включает выбор условий в древовидной структуре с возможностью обхода свойств связанных сущностей. Значение по умолчанию равно <literal>true</literal>. В случае задания значения <literal>false</literal> выбор условий осуществляется в плоском выпадающем списке из описателей условий, заданных в дескрипторе экрана.</para> + </listitem> + <listitem> + <para><property>cuba.allowQueryFromSelected</property> служит для включения механизма <link linkend="sequential_filter_para">последовательного наложения фильтров</link>. Значение по умолчанию равно <literal>true</literal>.</para> + </listitem> + </itemizedlist> + <para><emphasis role="bold">Параметры вызова экрана</emphasis></para> + <para>Существует возможность указать при вызове экрана, какой фильтр и с какими параметрами применить. Фильтр должен быть заранее создан в базе данных и иметь заполненное поле <database>code</database>.</para> + <para>Для указания кода фильтра в экран следует передать параметр с именем, равным <link linkend="attr_id">идентификатору</link> компонента фильтра в экране. Значение параметра − код фильтра, который нужно применить.</para> + <para>Для установки значений параметров фильтра нужно передать параметры с именами, равными <link linkend="component_filter_name_caution">именам параметров</link>, и значения в виде строк.</para> + <para>Пример установки фильтра из главного меню:</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/filter/filter_from_menu.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Следует отметить, что фильтр с установленным полем <database>code</database> обладает особыми свойствами:</para> + <itemizedlist> + <listitem> + <para>Его не могут редактировать пользователи</para> + </listitem> + <listitem> + <para>Название такого фильтра локализуется − в <link linkend="main_message_pack">главном пакете сообщений</link> приложения должна быть строка с ключом, равным коду фильтра.</para> + </listitem> + </itemizedlist> + <para id="sequential_filter_para"><emphasis role="bold">Последовательное наложение фильтров</emphasis></para> + <para>При включенном свойстве приложения <property> + <link linkend="cuba.allowQueryFromSelected">cuba.allowQueryFromSelected</link> + </property> в пользовательском интерфейсе компонента можно закреплять последний примененный фильтр и текущие результаты фильтрации. После этого можно выбрать другой фильтр или параметры и применить их на уже выбранных записях.</para> + <para>Данный подход позволяет решить две проблемы:</para> + <itemizedlist> + <listitem> + <para>Декомпозировать сложные фильтры.</para> + </listitem> + <listitem> + <para>Применять фильтры на записи, отобранные с помощью папок <link linkend="application_folder">приложения</link> или <link linkend="search_folder">поиска</link>.</para> + </listitem> + </itemizedlist> + <para>Чтобы применить этот механизм в пользовательском интерфейсе, выберите и примените один из фильтров. Затем нажмите на кнопку <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_filter_add.png"/> + </imageobject> + </inlinemediaobject>. Фильтр закрепится в нижней части панели фильтра. Далее можно применить к выбранным записям другой фильтр. Так последовательно можно накладывать друг на друга любое количество фильтров. Также фильтры можно удалять последовательно с помощью кнопки <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_filter_remove.png"/> + </imageobject> + </inlinemediaobject></para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_filter_sequential.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Механизм последовательного наложения фильтров основан на возможности <code> + <link linkend="dataService">DataWorker</link> + </code> выполнять <link linkend="query_from_selected">последовательные запросы</link>.</para> + <warning> + <para>На момент написания данного руководства механизм последовательного наложения фильтров реализован только для блока <structname>Web Client</structname>.</para> + </warning> + <para>Атрибуты <sgmltag>filter</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="applyTo_attr">applyTo</link> + </entry>editable<entry align="left"> + <link linkend="attr_id">id</link> + </entry><entry align="left"> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row> + <entry> + <link linkend="attr_datasource">datasource</link> + </entry> + <entry align="left"> + <link linkend="manualApplyRequired_attr">manualApplyRequired</link> + </entry> + <entry> + <link linkend="useMaxResults_attr">useMaxResults</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="editable_filter_attr">editable</link> + </entry> + <entry align="left"> + <link linkend="required_filter_attr">required</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты элемента <sgmltag>properties</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="1" colsep="1" rowsep="1" align="left"><colspec colname="c1"/>c <tbody> + <row><entry align="left"> + <link linkend="include_filter_attr">include</link> + </entry>editable</row> + <row> + <entry> + <link linkend="exclude_filter_attr">exclude</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты элемента <sgmltag>property</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="caption_filter_attr">caption</link> + </entry>editable<entry align="left"> + <link linkend="paramView_filter_attr">paramView</link> + </entry></row> + <row> + <entry> + <link linkend="name_filter_property_attr">name</link> + </entry> + <entry align="left"> + <link linkend="paramWhere_filter_attr">paramWhere</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты элемента <sgmltag>custom</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="caption_filter_custom_attr">caption</link> + </entry>editable<entry align="left">operatorType</entry><entry align="left"> + <link linkend="paramWhere_filter_attr">paramWhere</link> + </entry></row> + <row> + <entry> + <link linkend="join_filter_attr">join</link> + </entry> + <entry align="left"> + <link linkend="paramClass_filter_attr">paramClass</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="name_filter_custom_attr">name</link> + </entry> + <entry align="left"> + <link linkend="paramView_filter_attr">paramView</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + </section> + <section id="gui_layouts"> + <title>Контейнеры + BoxLayout + ButtonsPanel + GridLayout + ScrollBoxLayout + SplitPanel + GroupBoxLayout + TabSheet + IFrame +
+ BoxLayout + BoxLayout представляет собой контейнер с последовательным размещением компонентов. + Существует 3 типа BoxLayout, определяемых именем XML-элемента: + + + hbox − горизонтальное расположение компонентов +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_hbox.png"/> + </imageobject> + </mediaobject> + </figure> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/box/hbox.txt" encoding="UTF-8" parse="text"/></programlisting> + </listitem> + <listitem> + <para><sgmltag>vbox</sgmltag> − вертикальное расположение компонентов. <sgmltag>vbox</sgmltag> имеет 100% ширину по умолчанию</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="182" contentdepth="245" align="center" fileref="img/gui_vbox.png"/> + </imageobject> + </mediaobject> + </figure> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/box/vbox.txt" encoding="UTF-8" parse="text"/></programlisting> + </listitem> + <listitem> + <para><sgmltag>flowbox</sgmltag> − горизонтальное расположение компонентов с переносом вниз. При недостатке места по горизонтали непомещающиеся компоненты будут перенесены "на следующую строку" (поведение аналогично <application>Swing</application> <code>FlowLayout</code>)</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_flowbox.png"/> + </imageobject> + </mediaobject> + </figure> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/box/flowbox.txt" encoding="UTF-8" parse="text"/></programlisting> + </listitem> + </itemizedlist> + <para>Могут быть использованы следующие XML-атрибуты:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_align">align</link> + </entry>editable<entry align="left"> + <link linkend="attr_id">id</link> + </entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row> + <entry> + <link linkend="attr_expand">expand</link> + </entry> + <entry align="left"> + <link linkend="attr_margin">margin</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_spacing">spacing</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_ButtonsPanel"> + <title>ButtonsPanel + Компонент, унифицирующий использование и размещение кнопок для управления данными в таблице. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="272" align="center" fileref="img/gui_buttonsPanel.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>buttonsPanel</sgmltag>.</para> + <para>Для создания панели с кнопками нужно определить ее описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/buttonsPanel/buttonsPanel.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибут <sgmltag id="attr_alwaysVisible">alwaysVisible</sgmltag> служит для управления видимостью панели с кнопками при открытии окна списка в режиме поиска (например, при открытии окна списка из компонента <link linkend="gui_PickerField">PickerField</link>). Если значение атрибута равно <literal>true</literal>, то панель с кнопками не скрывается. По умолчанию значение атрибута равно <literal>false</literal>.</para> + <para>Элементами панели с кнопками являются элементы <link linkend="gui_Button">button</link>.</para> + <para>Атрибуты <sgmltag>buttonsPanel</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row> + <entry align="left"> + <link linkend="attr_align">align</link> + </entry> + <entry align="left"> + <link linkend="attr_id">id</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_alwaysVisible">alwaysVisible</link> + </entry> + <entry align="left"> + <link linkend="attr_stylename">styleName</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_expand">expand</link> + </entry> + <entry align="left"> + <link linkend="attr_visible">visible</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_height">height</link> + </entry> + <entry align="left"> + <link linkend="attr_width">width</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_GridLayout"> + <title>GridLayout + Контейнер, располагающий компоненты по сетке. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_gridlayout.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>grid</sgmltag>.</para> + <para>Для создания контейнера с табличным расположением нужно определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/grid/grid.txt" encoding="UTF-8" parse="text"/></programlisting> + <warning> + <para>Обязательными вложенными элементами являются элементы <sgmltag>columns</sgmltag> и <sgmltag>rows</sgmltag>. </para> + </warning> + <para><sgmltag id="attr_layout_rows">rows</sgmltag> − обязательный элемент, содержит последовательность строк (<sgmltag>row</sgmltag>). Хотя бы один элемент строки является обязательным.</para> + <para><sgmltag id="attr_layout_row">row</sgmltag> − элемент строки, контейнер для содержимого ряда. Состоит из контейнеров или компонентов.</para> + <para><sgmltag id="attr_grid_flex_row">flex</sgmltag> − необязательный атрибут для элемента <sgmltag>row</sgmltag>, задает соотношение высот рядов сетки.</para> + <para><sgmltag id="attr_grid_columns">columns</sgmltag> − обязательный элемент, содержит последовательность <sgmltag>column</sgmltag>.</para> + <para>Атрибут <sgmltag id="attr_grid_count">count</sgmltag> − необязательный атрибут, задает количество колонок, если не заданы элементы <sgmltag>column</sgmltag>.</para> + <para><sgmltag id="attr_grid_column">column</sgmltag> − необязательный элемент для элемента <link linkend="attr_grid_columns">columns</link>, декларирует атрибуты колонок сетки.</para> + <para>Атрибут <sgmltag id="attr_grid_flex_column">flex</sgmltag> − необязательный атрибут, задает соотношение ширин колонок.</para> + <para>Атрибуты <sgmltag>grid</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_height">height</link> + </entry>editable<entry align="left"> + <link linkend="attr_spacing">spacing</link> + </entry><entry> + <link linkend="attr_width">width</link> + </entry></row> + <row> + <entry> + <link linkend="attr_id">id</link> + </entry> + <entry align="left"> + <link linkend="attr_stylename">styleName</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_margin">margin</link> + </entry> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Элементы <sgmltag>grid</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_grid_columns">columns</link> + </entry>editable</row> + <row> + <entry> + <link linkend="attr_layout_rows">rows</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты <sgmltag>columns</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_grid_count">count</link> + </entry>editable</row> + </tbody></tgroup> + </informaltable> + <para>Атрибуты <sgmltag>row</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_align">align</link> + </entry>editable</row> + <row> + <entry> + <link linkend="attr_grid_flex_row">flex</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_spacing">spacing</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_visible">visible</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_ScrollBoxLayout"> + <title>ScrollBoxLayout + ScrollBoxLayout − контейнер, который позволяет прокручивать свое содержимое. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_scrollBox.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>scrollBox</sgmltag></para> + <para>Для создания контейнера с табличным расположением нужно определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/scrollBox/scrollBox.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>С помощью атрибута <sgmltag id="attr_scroll_orientation">orientation</sgmltag> можно задавать направление расположения вложенных компонентов − <literal>horizontal</literal> или <literal>vertical</literal>. По умолчанию значением атрибута является <literal>vertical</literal>.</para> + <warning> + <para>Вложенные в <sgmltag>scrollBox</sgmltag> компоненты должны иметь фиксированные размеры или размеры по умолчанию. Нельзя устанавливать <code>height="100%"</code> или <code>width="100%"</code>.</para> + </warning> + <para>Атрибуты <sgmltag>scrollBox</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_align">align</link> + </entry><entry> + <link linkend="attr_scroll_orientation">orientation</link> + </entry>editable</row> + <row> + <entry> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_spacing">spacing</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_id">id</link> + </entry> + <entry> + <link linkend="attr_stylename">stylename</link> + </entry> + </row> + <row> + <entry> + <link linkend="attr_margin">margin</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_SplitPanel"> + <title>SplitPanel + Панель с разделителем (SplitPanel) − контейнер, разбитый на две области, размер которых по одному из направлений (горизонтали либо вертикали) можно менять путем перемещения разделителя. На рисунке показан пример разбивки области окна на три панели. Во внешней панели формируется горизонтальная граница, которая отделяет нижнюю панель от верхней. Верхняя панель разделяется по вертикали. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_splitPanel.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>split</sgmltag>.</para> + <para>Для создания панели с разделителем нужно определить ее описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/split/split.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>При создании панели с разделителем нужно указать ориентацию разделителя с помощью атрибута <sgmltag id="attr_layout_orientation">orientation</sgmltag>. По умолчанию значение атрибута равно <literal>vertical</literal>.</para> + <para id="attr_layout_pos"><sgmltag>pos</sgmltag> − необязательный атрибут, определяет процентное соотношение областей. Например, <code>pos="30%"</code> означает соотношение областей 30/70. По умолчанию соотношение областей составляет 50/50.</para> + <warning> + <para>Обязательными вложенными элементами являются два контейнера или компонента.</para> + </warning> + <para>Атрибуты <sgmltag>split</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_id">id</link> + </entry>editable<entry align="left"> + <link linkend="attr_layout_pos">pos</link> + </entry></row> + <row> + <entry> + <link linkend="attr_height">height</link> + </entry> + <entry align="left"> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_layout_orientation">orientation</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_GroupBoxLayout"> + <title>GroupBox + Контейнер, позволяющий выделить рамкой группу объектов, задать им общий заголовок и описание. Кроме того, он умеет сворачивать свое содержимое. +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="188" contentdepth="270" align="center" fileref="img/gui_groupBox.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>groupBox</sgmltag>.</para> + <para>Для создания контейнера с названием и рамкой нужно определить его описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/groupBox/groupBox.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Атрибут <sgmltag id="attr_group_orientation">orientation</sgmltag> задает направление расположения вложенных компонентов − <literal>horizontal</literal> или <literal>vertical</literal>. По умолчанию значением атрибута является <literal>vertical</literal>.</para> + <para><sgmltag id="attr_group_collapsable">collapsable</sgmltag> − необязательный атрибут, в значении <literal>true</literal> компонент может сворачивать свое содержимое. В верхнем нижнем углу контейнера появляется значок <inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_groupBox_minus.png"/> + </imageobject> + </inlinemediaobject>/<inlinemediaobject> + <imageobject> + <imagedata fileref="img/gui_groupBox_plus.png"/> + </imageobject> + </inlinemediaobject>.</para> + <para><sgmltag id="attr_group_collapsed">collapsed</sgmltag> − необязательный атрибут, в значении <literal>true</literal> компонент будет свернут по умолчанию.</para> + <figure> + <title/> + <mediaobject> + <imageobject> + <imagedata contentwidth="80%" align="center" fileref="img/gui_groupBox_collapsed.png"/> + </imageobject> + </mediaobject> + </figure> + <para>Атрибуты <sgmltag>groupBox</sgmltag>:</para> + <informaltable frame="none" pgwide="0" align="left"> + <tgroup cols="4" colsep="1" rowsep="1" align="left"><colspec colname="c1"/><colspec colname="c2"/><colspec colname="c3"/><colspec colname="c4"/>c <tbody> + <row><entry align="left"> + <link linkend="attr_caption">caption</link> + </entry>editable<entry align="left"> + <link linkend="attr_description">description</link> + </entry><entry> + <link linkend="attr_id">id</link> + </entry><entry> + <link linkend="attr_stylename">stylename</link> + </entry></row> + <row> + <entry> + <link linkend="attr_group_collapsable">collapsable</link> + </entry> + <entry align="left"> + <link linkend="attr_expand">expand</link> + </entry> + <entry> + <link linkend="attr_orientation">orientation</link> + </entry> + <entry> + <link linkend="attr_width">width</link> + </entry> + </row> + <row> + <entry align="left"> + <link linkend="attr_group_collapsed">collapsed</link> + </entry> + <entry> + <link linkend="attr_height">height</link> + </entry> + <entry> + <link linkend="attr_spacing">spacing</link> + </entry> + </row> + </tbody></tgroup> + </informaltable> + </section> + <section id="gui_TabSheet"> + <title>TabSheet + Панель с вкладками (TabSheet) позволяет выводить на экран вкладки (tabs) − панели, имеющие название. При нажатии пользователем на названии вкладки TabSheet выводит на экран соответствующие выбранной вкладке элементы пользовательского интерфейса. +
+ + <mediaobject> + <imageobject> + <imagedata align="center" fileref="img/gui_tabsheet.png"/> + </imageobject> + </mediaobject> + </figure> + <para>XML-имя компонента: <sgmltag>tabSheet</sgmltag>.</para> + <para>Для создания панели с вкладками нужно определить ее описание в <glossterm linkend="screen_xml_glossentry">xml-дескрипторе</glossterm>.</para> + <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="source/section_gui/tabsheet/tabsheet.txt" encoding="UTF-8" parse="text"/></programlisting> + <para>Как видно, компонент <sgmltag>tabSheet</sgmltag> состоит из элементов <sgmltag>tab</sgmltag>.</para> + <para>Элемент <sgmltag>tab</sgmltag> может содержать как другой <link linkend="gui_layouts">контейнер</link>, так и <link linkend="gui_components">компонент</link>.</para> + <para>Атрибут <link linkend="attr_id" id="attr_tabsheet_id">id</link> является идентификатором вкладки. Следует отметить, что вкладка не является компонентом, и данный идентификатор используется только в рамках <code>TabSheet</code>.</para> + <para>Атрибут <sgmltag id="attr_tabsheet_lazy">lazy</sgmltag> задает отложенную загрузку содержимого вкладки. При открытии экрана <sgmltag>lazy</sgmltag>-вкладки не загружают свое содержимое, что приводит к созданию меньшего количества компонентов в памяти и, как следствие, к меньшему количеству запросов к БД. Компоненты вкладки загружаются только в тот момент, когда пользователь выбирает данную вкладку. Значение по умолчанию равно <literal>false</literal>. Если компоненты <sgmltag>lazy</sgmltag>-вкладки требуют инициализации, проводить ее нужно не напрямую в методе <code>init()</code> <glossterm linkend="screen_controller_glossentry">контроллера</glossterm>, а в слушателе на переключение вкладок <code>TabSheet.TabChangeListener.</code></para> + <para>В десктоп-приложении есть возможность открывать вкладки в новом окне. Для этого нужно атрибуту <sgmltag id="attr_detachable">detachable</sgmltag> присвоить значение <literal>true</literal>.</para> + <figure> + <title>Вид отделяемой вкладки + + + + + +
+ Атрибуты tabSheet: + + c + + height + editable + visible + + + + id + + + width + + + + + stylename + + + + + Атрибуты элемента tab: + + c + + caption + editable + expand + + margin + + + + detachable + + + id + + + spacing + + + + + enable + + + lazy + + + + +
+
+ IFrame + Фреймы предназначены для декомпозиции и многократного использования частей экранов. + Фрейм включается в экран с помощью элемента iframe. + Для включения фрейма в экран необходимо в xml-дескрипторе определить его описание: + + Атрибут src − путь к XML-дескриптору фрейма. + Если фрейм зарегистрирован в файле screens.xml, то можно указать идентификатор фрейма в атрибуте screen. + + Обязательно должен быть указан один из атрибутов: src или screen. + + Дескриптор фрейма идентичен xml-дескриптору экрана и может содержать собственный metadataContext, dsContext, messagesPack. Кроме того, фрейм может иметь собственный контроллер. + Правила взаимодействия экрана и вложенного в него фрейма: + + + Из экрана обращаться к компонентам фрейма можно через точку: frame_id.component_id + + + Из контроллера фрейма получить компонент экрана можно обычным вызовом getComponent(component_id), но только в том случае, если компонент с таким именем не объявлен в самом фрейме. То есть компоненты фрейма маскируют компоненты экрана. + + + Из фрейма получить источник данных экрана можно простым вызовом getDsContext().get(ds_id) либо в запросе ds$ds_id, но только в том случае, если источник данных с таким именем не объявлен в самом фрейме (аналогично компонентам). + + + Из экрана получить источник данных фрейма можно только через итерацию по getDsContext().getChildren() + + + При коммите экрана вызывается также коммит измененных источников данных фрейма. + Атрибуты iframe: + + c + + align + editable + screen + + visible + + + + height + + + src + + + width + + + + + id + + + stylename + + + + +
+
+
+ Разное +
+ AccessControl + Компонент accessControl предназначен для декларативного управления доступом к частям экрана. Он представляет собой контейнер, который никак не отображает себя на экране, а только управляет атрибутами visible и editable входящих в него компонентов. + Состояние управляемых компонентов определяется на основе двух элементов, вложенных в accessControl: visible и editable. + Соответствующее значение принимается либо из скрипта, возвращающего boolean, либо из свойства объекта AccessData, связанного с компонентом (см. ниже). +Скрипт задается либо атрибутом script, либо in-place в тексте элемента. +Свойство объекта AccessData задается атрибутом property и имеет больший приоритет, чем скрипт. + + В случае visible=false вложенные компоненты вообще не создаются, поэтому будьте осторожны при обращении к ним из контроллера. + + + <tab id="mainTab" caption="msg://mainTab"> + <accessControl data="workflow.client.web.ui.card.CardAccessData" param="accessData"> + <editable property="notStarted"/> + + <vbox margin="true" expand="attachmentsPane"> + ... + +
+ Объект AccessData + В связи с тем, что одни и те же параметры доступа могут многократно потребоваться в разных частях экрана и в коде контроллера, компонент accessControl может быть связан с объектом Java|Groovy, вычисляющим и хранящим эти параметры. Т.о. дорогостоящая инициализация параметра доступа производится только один раз, и затем доступна на протяжении жизни экрана. + Для связи компонента с данными доступа создайте класс, унаследованный от AbstractAccessData, и задайте следующие атрибуты компонента accessControl: + + + data − имя класса AccessData + + + param − имя параметра экрана, в котором будет сохранен объект AccessData + + + В момент создания экрана загрузчик компонента accessControl проверяет наличие экземпляра AccessData в указанном параметре, и если его там нет, создает новый экземпляр. + В дальнейшем в коде контроллера можно получить экземпляр AccessData из параметров экрана, например: + AbstractWfAccessData accessData = getContext().getParamValue("accessData"); + При создании класса AccessData следует иметь в виду, что в параметры экрана-редактора всегда передается параметр param$item, содержащий текущий редактируемый экземпляр сущности. Однако этот экземпляр загружен по view вызывающего экрана, а не по view редактора. + Свойства AccessData, на которые можно ссылаться из компонента accessControl, должны быть реализованы по стандарту JavaBeans: методами с сигнатурой boolean getXxx() + + + +
+
+
+ Action + Action − интерфейс, абстрагирующий действие от визуального компонента. + Визуальный компонент, содержащий одно действие, реализует интерфейс Component.ActionOwner. Это, например, Button. + Визуальный компонент, содержащий несколько действий, реализует интерфейс Component.ActionsHolder. Это Window, IFrame, Table и ее наследники, Tree, PopupButton, PickerField, LookupPickerField. +
+ Декларативное создание действий + В дескрипторе экрана может быть задан набор действий для некоторого ActionsHolder. + + Доступные атрибуты действия + + id − идентификатор, должен быть уникален в рамках данного ActionsHolder + + + caption + + + icon + + + enable + + + visible − в отличие от компонентов Groovy-выражения не поддерживаются + + + invoke − имя вызываемого метода контроллера. Метод должен быть public, не возвращать результата, и либо не иметь аргументов, либо иметь один аргумент типа Component. Если метод имеет аргумент Component, то при вызове в него будет передан экземпляр визуального компонента, запустившего данное действие. + + + shortcut − доступен только для действий, задаваемых в окне или фрейме. Задает комбинацию клавиш для вызова. + + + Примеры: + + + +
+
+ Использование в кнопках + Действия, заданные для некоторого ActionsHolder, могут быть использованы в компонентах ActionOwner, например в Button. Для этого достаточно указать полное имя действия в соотв. атрибуте. Например: + + При этом Button возьмет значения атрибутов caption, icon, enable, visible из назначенного действия. +
+
+ Стандартные действия + Для наследников ListComponent (это Table и Tree) существует набор стандартных действий, определяемых перечислением ListActionType. Для таких действий не нужно определять никаких атрибутов, кроме идентификатора. Например: + + Для компонентов PickerField и LookupPickerField существует набор стандартных действий, определяемых перечислением PickerField.ActionType. Для таких действий не нужно определять никаких атрибутов, кроме идентификатора. Например: + +
+
+ Программное создание и управление действиями + Базовым классом реализации действий является AbstractAction. Пример использования: + + При выполнении метода addAction() реализация ActionsHolder проверяет, нет ли уже в нем действия с таким же идентификатором. Если есть, то имеющееся действие будет заменено на новое переданное. Поэтому можно безопасно, например, декларировать стандартное действие в дескрипторе экрана, а затем в контроллере создать новое с переопределенными методами, и добавить в ActionsHolder. + Кроме создания наследников AbstractAction или стандартных действий, возможно конфигурирование декларативно созданных действий путем получения их в контроллере и установки атрибутов. Например: + + При этом во всех визуальных компонентах, связанных с данным действием, значение соответствующего атрибута также изменится. +
+
+
+ Aggregation + Механизм задания агрегаций для колонок в Table. + + Атрибуты aggregation + + type − функция агрегирования: + + + SUM − сумма + + + AVG − среднее значение + + + COUNT − количество + + + MIN − минимальное значение + + + MAX − максимальное значение + + + + + formatter − задает формат вывода агрегированного значения. + + + Логика применения форматирования к агрегированному значению следующая: если в элементе aggregation указан formatter, то применяется он; иначе применяется formatter соответствующего агрегированному значению datatype. + + + +
+
+ Presentation + TODO +
+
+
+ Элементы XML + + + formatter + + XML-элемент formatter задает класс, который будет применен для преобразования значения в строку. + Атрибуты: + + + class − имя класса, реализующего интерфейс com.haulmont.cuba.gui.components.Formatter + + + + + + validator + + XML-элемент для задания механизма валидации значений, введенных в визуальном компоненте. + Атрибуты: + + + script − путь к скрипту Groovy, осуществляющему валидацию + + + class − имя класса Java, реализующего интерфейс Field.Validator + + + message − сообщение, выводимое пользователю в случае ошибки валидации. Атрибут должен содержать ключ сообщения в пакете, например, message="msg://infoTextField.validationMsg" + + + Выбор механизма валидации осуществляется следующим образом: + + + Если не указано значение атрибута script, и сам элемент validator не содержит текста выражения Groovy, то в качестве валидатора используется класс, указанный в атрибуте class. + + + Если элемент validator содержит текст, то он будет использован как выражение Groovy и выполнен с помощью Scripting. + + + В противном случае с помощью Scripting будет выполнен скрипт Groovy, указанный в атрибуте script. + + + В выражение или скрипт Groovy будет передана одна переменная value, содержащая значение, введенное в визуальном компоненте. + Выражение или скрипт должны вернуть boolean значение: true − valid, false − not valid. + CUBA уже содержит несколько реализаций наиболее часто используемых валидаторов (см. пакет com.haulmont.cuba.gui.components.validators), которые можно применять в своих проектах: + + + DateValidator + + + DoubleValidator + + + EmailValidator + + + IntegerValidator + + + LongValidator + + + PatternValidator + + + ScriptValidator + + + Примеры использования: + <field id="imei"> + <validator class="com.haulmont.cuba.gui.components.validators.PatternValidator" + pattern="\d{15}" + message="msg://general.imeiValidationFailed"/> +</field> + +<field id="maxCountOfVisits"> + <validator class="com.haulmont.cuba.gui.components.validators.IntegerValidator"/> +</field> + + + +
+
+ Атрибуты XML + + + align + + Атрибут, задающий расположение компонента относительно вышестоящего контейнера. + Возможные значения: + + + TOP_RIGHT + + + TOP_LEFT + + + TOP_CENTER + + + MIDDLE_RIGHT + + + MIDDLE_LEFT + + + MIDDLE_CENTER + + + BOTTOM_RIGHT + + + BOTTOM_LEFT + + + BOTTOM_CENTER + + + + + + caption + + XML-атрибут, устанавливающий заголовок для визуального компонента. + Значением атрибута должна быть либо собственно строка сообщения, либо ключ в пакете сообщений. В случае ключа значение должно начинаться с префикса msg:// + Способы задания ключа: + + + Короткий ключ − при этом сообщение ищется в пакете, заданном для данного экрана: + caption="msg://infoFieldCaption" + + + Полный ключ, с заданием пакета: + caption="msg://com.haulmont.refapp.gui.app/infoFieldCaption" + + + + + + captionProperty + + XML-атрибут визуального компонента, реализующего интерфейс OptionsField. + Задает имя атрибута сущности, которую содержит источник данных, используемый для формирования списка опций (optionsDatasource). + Если данный атрибут не задан, список опций будет содержать Instance Name экземпляров, содержащихся в списке. + + + + clickAction + + Атрибут содержит описание действия, которое будет выполнено при клике в ячейке или в поле (для компонента FieldGroup). Возможны два типа действий: + + + open − открывает для сущности, отображаемой в ячейке, экран редактирования с указанным именем, например: clickAction="open:sec$User.edit". Имя сущности отображается в виде ссылки: +
+ + <mediaobject> + <imageobject> + <imagedata contentwidth="40%" align="center" fileref="img/gui_clickAction_open.png"/> + </imageobject> + </mediaobject> + </figure> + </listitem> + <listitem> + <para><code>invoke</code> − вызывает метод <glossterm linkend="screen_controller_glossentry">контроллера</glossterm> экрана с указанным именем, например: <code>clickAction="invoke:onClick"</code>. Метод должен иметь единственный параметр типа <type>Object</type>, в который будет передан экземпляр сущности, отображаемой в ячейке.</para> + </listitem> + </itemizedlist> + </listitem> + </varlistentry> + <varlistentry id="attr_datasource"> + <term>datasource</term> + <listitem> + <para>XML-атрибут компонента, реализующего интерфейс <code>DatasourceComponent</code>.</para> + <para>Предназначен для задания <link linkend="datasources">источника данных</link> и должен содержать имя источника данных, описанного в секции <parameter>dsContext</parameter> <link linkend="screen_xml_glossentry">дескриптора</link> экрана.</para> + <para>Атрибут property является обязательным, иначе возникает ошибка java.lang.IllegalStateException: Can't set assign datasource 'sampleDs' for component 'labelInfo' due 'property' attribute is not defined. </para> + <para>Смотрите также атрибут property.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_description"> + <term>description</term> + <listitem> + <para>Атрибут, задающий текст подсказки для компонента.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_editable"> + <term>editable</term> + <listitem> + <para>XML-атрибут, указывающий на возможность редактирования содержимого (не путать с <link linkend="attr_enable">enable</link>).</para> + <para>Возможные значения − true, false. По умолчанию true.</para> + </listitem> + <listitem>На возможность редактирования содержимого для компонента, связанного с данными (наследника DatasourceComponent или List), влияет также Security. Если по данным security данный компонент должен быть недоступен для редактирования, значение атрибута editable не принимается во внимание.</listitem> + </varlistentry> + <varlistentry id="attr_enable"> + <term>enable</term> + <listitem> + <para>Атрибут компонента, устанавливающий его состояние "enabled/disabled" − доступен/недоступен.</para> + <para>Если компонент недоступен, то он не принимает фокус ввода. Недоступность контейнера приводит к тому, что все его компоненты также становятся недоступными. + +Возможные значения − true, false. + +По умолчанию у всех компонентов enable = true.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_expand"> + <term>expand</term> + <listitem> + <para>Атрибут контейнера для управления его внутренней компоновкой.</para> + <para>Задает компонент внутри контейнера, который необходимо "развернуть", т.е. установить ему максимально возможную высоту и ширину.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_height"> + <term>height</term> + <listitem> + <para>Атрибут, устанавливающий высоту компонента.</para> + <para>Может быть задана в пикселях либо в процентах от высоты вышестоящего контейнера. Например: 100px, 100%, 50. Если единица измерения не указана, подразумевается высота в пикселях.</para> + <para>Простановка значения в % означает, что компонент по высоте займет соответствующую часть пространства, предоставляемого контейнером более высокого уровня.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_icon"> + <term>icon</term> + <listitem> + <para>XML-атрибут, устанавливающий пиктограмму для визуального компонента.</para> + <para>Значением атрибута должен быть путь к файлу пиктограммы относительно каталога темы. Например:</para> + <programlisting>icon="icons/create.png"</programlisting> + <para>Если пиктограмма должна быть выбрана в зависимости от языка пользователя, можно указать путь к ней в пакете сообщений, а в атрибуте icon − ключ сообщения, например:</para> + <programlisting>icon="msg://addIcon"</programlisting> + </listitem> + </varlistentry> + <varlistentry id="attr_id"> + <term>id</term> + <listitem> + <para>Идентификатор компонента.</para> + <para>Возможное значение − любой допустимый Java-идентификатор. Рекомендуется использовать только camelСase, например, <code>userGrid</code>, <code>filterPanel</code>. + +Может быть указан для любого компонента и должен быть уникальным в пределах экрана.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_margin"> + <term>margin</term> + <listitem> + <para>Атрибут margin устанавливает наличие отступа между внешними границами и содержимым контейнера.</para> + <para>Может иметь 2 вида значений:</para> + <itemizedlist> + <listitem> + <para>margin="true" − установить отступ со всех сторон сразу</para> + </listitem> + <listitem> + <para>margin="true;false;true;false;" − установить отступ только сверху и снизу (формат значения "сверху,справа,снизу,слева")</para> + </listitem> + </itemizedlist> + <para>По умолчанию отступы отсутствуют.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_optionsDatasource"> + <term>optionsDatasource</term> + <listitem> + <para>XML-атрибут визуального компонента, реализующего интерфейс <code>OptionsField</code>.</para> + <para>Задает имя <link linkend="datasources">источника данных</link>, используемого для формирования списка опций.</para> + <para>Совместно с optionsDatasource может использоваться атрибут <link linkend="attr_captionProperty">captionProperty</link>.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_property"> + <term>property</term> + <listitem> + <para>XML-атрибут компонента, реализующего интерфейс <code>DatasourceComponent</code>.</para> + <para>Предназначен для задания имени атрибута сущности, значение которого будет отображаться/редактироваться данным визуальным компонентом.</para> + <para>Используется всегда совместно с атрибутом <link linkend="attr_datasource">datasource</link>.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_required"> + <term>required</term> + <listitem> + <para>XML-атрибут визуального компонента, реализующего интерфейс Field. Указывает, что в данное поле обязательно должно быть введено значение.</para> + <para>Возможные значения атрибута − true, false. По умолчанию false.</para> + <para>Совместно с required может использоваться атрибут requiredMessage.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_requiredMessage"> + <term>requiredMessage</term> + <listitem> + <para>XML-атрибут, используемый совместно с атрибутом <link linkend="attr_required">required</link>. Позволяет установить сообщение, выводимое пользователю в случае нарушения требования <link linkend="attr_required">required</link>.</para> + <para>Атрибут должен содержать ключ сообщения в пакете, например: requiredMessage="msg://infoTextField.requiredMessage"</para> + </listitem> + </varlistentry> + <varlistentry id="attr_spacing"> + <term>spacing</term> + <listitem> + <para>Атрибут spacing устанавливает наличие отступов между компонентами внутри контейнера.</para> + <para>Возможные значения − true, false.</para> + <para>По умолчанию отступы отсутствуют.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_stylename"> + <term>stylename</term> + <listitem> + <para>Атрибут, задающий имя стиля компонента.</para> + <para>Для веб-клиента стиль задается в CSS.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_visible"> + <term>visible</term> + <listitem> + <para>Атрибут либо элемент видимости компонента.</para> + <para>Возможные значения − true, false, либо выражение Groovy с boolean-результатом. Если выражение длинное, имеет смысл использовать не атрибут, а элемент visible внутри текущего компонента − эффект тот же. Если контейнер невидим, не видны и все его компоненты. По умолчанию все компоненты видимы.</para> + </listitem> + </varlistentry> + <varlistentry id="attr_width"> + <term>width</term> + <listitem> + <para>Атрибут, устанавливающий ширину компонента.</para> + <para>Значение может быть задано в пикселях или в процентах от ширины вышестоящего контейнера. Например: 100px, 100%, 50. Если единица измерения не указана, подразумевается ширина в пикселях. Простановка значения в % означает, что компонент по ширине займет соответствующую часть пространства, предоставляемого контейнером более высокого уровня.</para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section id="gui_themes"> + <title>Создание темы приложения + TODO +
+
+
+ Источники данных + Предназначены для реализации связанных с данными (data-aware) компонентов. + Существуют следующие интерфейсы источников данных: + + + Datasource − предназначен для работы с одним экземпляром сущности. + + + RuntimePropsDatasource − предназначен для работы с динамическими атрибутами сущностей. + + + + + CollectionDatasource − предназначен для работы с коллекцией сущностей + + + HierarchicalDatasource − предназначен для работы с компонентами + Tree + , + TreeTable + . + + + GroupDatasource − предназначен для работы с компонентом + GroupTable + . + + + + + Как правило, источники данных объявляются декларативно в секции dsContext дескриптора экрана. +
+
+ Фоновые задачи + Предназначение + Фоновые задачи используются на клиентском уровне для выполнения длительных процессов без заморозки пользовательского интерфейса. + В платформе реализован объект BackgroundWorker, который предоставляется окружением и управляет фоновыми задачами + BackgroundWorker backgroundWorker = AppConfig.getBackgroundWorker(); + Использование + + + Задача описывается как наследник абстрактного класса BackgroundTask. Для нее необходимо задать окно, которому принадлежит задача, и описать главный метод run(). + + + Создается объект управления задачей − BackgroundTaskHandler. Для этого задачу необходимо передать классу BackgroundWorker. + + + Выполняется запуск задачи + // Задача с ограничением 10 секунд и с текущим окном в качестве родителя +final BackgroundTask<Integer, Void> progressIndicator = new BackgroundTask<Integer, Void>(10, this) { + @Override + public Void run(TaskLifeCycle<T> taskLifeCycle) { + // 1 2 3 4 5 :-) + for (int i = 0; i < 5; i++) { + Long res; + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException ignored) { + return null; + } + // Оглашаем прогресс + taskLifeCycle.publish(i); + } + return null; + } + + @Override + public void canceled() { + // отменено + } + + @Override + public void done(Void result) { + // завершено + } + + @Override + public void progress(List<Integer> changes) { + // индикация прогресса + } + + @Override + public Map<String, Object> getParams() { + // передаём параметры + return Collections.emptyMap(); + } +}; + +// Получение управляющего объекта и запуск +BackgroundTaskHandler taskHandler = backgroundWorker.handle(progressIndicator); +taskHandler.execute(); + + + Объект задачи + BackgroundTask<T, V> − параметризованный класс: + + + T − тип объектов, показывающих прогресс задачи. Они передаются в метод progress() при вызове publish() в рабочем потоке + + + V − тип результата задачи, его можно получить после выполнения задачи или вызвать синхронно getResult() для ожидания. + + + Метод canceled() вызывается только в случае управляемой отмены задачи (то есть при вызове cancel() у TaskHandler). + Если у задачи истек таймаут, или было закрыто окно, в котором она исполнялась, то задача будет завершена без уведомлений. + + Следует помнить, что в Java невозможно прервать поток, если он не использует операций, выбрасывающих InterrruptedException. Никогда не перехватывайте это исключение или все исключения с целью тихо завершить операцию. Хорошим тоном является проверка флага isInterrupted() у объекта TaskLifeCycle в различных циклических операциях, для того чтобы вовремя отменить выполнение при прерывании задачи. + + Объекты BackgroundTask не имеют состояния. Если придерживаться этого подхода и не заводить полей для хранения промежуточных данных, то можно использовать множество параллельно работающих задач, используя всего один объект задачи. + Объект BackgroundHandler можно запускать всего один раз; если требуется частый перезапуск задач, то используйте BackgroundTaskWrapper + Отображение фоновых действий для пользователя + Иногда необходимо показывать пользователю окно с прогрессом и кнопкой Отмена. Для этого есть BackgroundWorkWindow<T,V> с набором статических методов. +В окне можно отображать статус задачи и разрешать/запрещать отмену фонового процесса. + Отслеживание исполнения задач + Если Вы хотите использовать параметры из UI компонентов, то необходимо переопределить метод Map<String, Object> getParams() . Он выполняется один раз при запуске задачи в потоке UI. В методе run они доступны в объекте TaskLifeCycle, аксессор − getParams(). + При возникновении исключительных ситуаций вызывается метод handleException, в котором можно отобразить ошибку на UI. + Для отмены и удаления зависших задач предусмотрены следующие меры: + + + WatchDog − поток, постоянно проверяющий задачи на истечение таймаута. Зависшие задачи прерываются и удаляются из обработки + + + При закрытии родительского окна задачи она прерывается + + + По истечению сессии пользователя все его задачи прерываются. +Для этого в web.xml указать: + <listener> + <listener-class>com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener</listener-class> +</listener> + + + Объявление WatchDog + В app-web-spring.xml и app-desktop-spring.xml добавить объявление задачи по расписанию: + <bean id="backgroundWorkerScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"> + <property name="daemon" value="true"/> + <property name="poolSize" value="1"/> +</bean> + +<task:scheduled-tasks scheduler="backgroundWorkerScheduler"> + <task:scheduled ref="cuba_BackgroundWorker_WatchDog" method="cleanupTasks" fixed-delay="2000"/> +</task:scheduled-tasks> + Настройки + Для Web слоя в WebConfig настраивается частота проверки изменений на стороне клиента (браузера): cuba.backgroundWorker.uiCheckInterval (По умолчанию 2000 мс) +
+
+ Таймеры + TODO +
+
+
+ Компоненты портала + TODO +
+
+ Механизмы платформы +
+ Выполнение задач по расписанию + TODO +
+
+ Отсылка email + TODO +
+
+ Динамические атрибуты + TODO +
+
+ Пессимистичная блокировка + TODO +
+
+ Статистика сущностей + TODO +
+
+ Аудит изменений сущностей + TODO +
+
+ Снимки сущностей + TODO +
+
+ REST API + TODO +
+
+ Хранилище файлов + TODO +
+
+ Организация кэшей данных + TODO +
+
+ Выполнение SQL с помощью QueryRunner + TODO +
+
+ Интеграция с MyBatis + TODO +
+
+ Панель папок +
+ Папки поиска + Папки поиска обладают следующими особенностями: + + + при выборе отображают экраны с универсальным фильтром; + + + создаются пользователем из любого отображенного экрана с универсальным фильтром; + + + пользователь может изменить иерархию, название, либо переопределить фильтр поиска. + + +
+
+ Папки приложения + Обладают следующими особенностями: + + + при выборе отображают предопределенные экраны с фильтром или без; + + + набор папок может зависеть от ролей пользователя; + + + пользователь не может изменить настройки папок; + + + в заголовке папки может отображаться текущее количество входящих в нее записей; + + + периодически обновляются по таймеру. + + +
+
+
+ Инспектор сущностей + TODO +
+
+ Информация об используемом ПО + TODO +
+
+
+ Расширение функциональности + TODO +
+ Расширение сущностей + TODO +
+
+ diff --git a/doc/content/manual/ru/manual.xml b/doc/content/manual/ru/manual.xml index f30b33827c..a0ecad8b9d 100644 --- a/doc/content/manual/ru/manual.xml +++ b/doc/content/manual/ru/manual.xml @@ -1010,6 +1010,15 @@ menu-config.sales$Customer.lookup=Customers Используется в блоках: Middleware, Web Client, Web Portal. + + cuba.useLocaleLanguageOnly + + Если данное свойство имеет значение true, то при поиске локализованных сообщений, принимается во внимание только язык текущей или переданной локали. В противном случае принимаются во внимание все параметры локали, т.е. например можно определить файлы сообщений messages_en_US.properties и messages_en_GB.properties. + Значение по умолчанию: true + Интерфейс: GlobalConfig + Используется во всех стандартных блоках. + + cuba.user.fullNamePattern