CUBA documentation (datasources) #PL-1633

This commit is contained in:
Konstantin Krivopustov 2013-01-29 15:59:34 +00:00
parent ebd0380c80
commit 4556edc049

View File

@ -2412,6 +2412,15 @@ CustomerGrade.STANDARD=Стандартный</programlisting></para>
</property>.</para>
<para>Таким образом, идентификатор сессии, создаваемой при входе пользователя в систему, служит для аутентификации пользователя при каждом вызове среднего слоя.</para>
<para>Объект <code>UserSession</code> содержит также методы для <firstterm>авторизации</firstterm> текущего пользователя, т.е. проверки его прав на объекты системы: <code>isScreenPermitted()</code>, <code>isEntityOpPermitted()</code>, <code>isEntityAttrPermitted()</code>, <code>isSpecificPermitted()</code>.</para>
<para>С объектом <code>UserSession</code> могут быть ассоциированы именованные атрибуты произвольного сериализуемого типа. Атрибуты устанавливаются методом <code>setAttribute()</code> и возвращаются методом <code>getAttribute()</code>. Последний может также возвращать следующие параметры сессии, как если бы они были атрибутами:<itemizedlist>
<listitem>
<para><code>userId</code> - ID текущего зарегистрированного или замещенного пользователя;</para>
</listitem>
<listitem>
<para><code>userLogin</code> - логин текущего зарегистрированного или замещенного пользователя в нижнем регистре.</para>
</listitem>
</itemizedlist></para>
<para>Атрибуты реплицируются в кластере Middleware так же как и все остальные данные сессии.</para>
</section>
<section>
<title>Вход в систему</title>
@ -2434,7 +2443,7 @@ CustomerGrade.STANDARD=Стандартный</programlisting></para>
<para>Алгоритм хэширования паролей реализуется бином типа <code>EncryptionModule</code> и задается в свойстве приложения <property>
<link linkend="cuba.passwordEncryptionModule">cuba.passwordEncryptionModule</link>
</property>. По умолчанию - SHA-1.</para>
<para>Возможен вариант, когда пароль пользователя (точнее, хэш пароля) не хранится в базе данных, а проверяется внешними средствами, например путем интеграции с <application>ActiveDirectory</application>. В этом случае фактически аутентификацию выполняет клиентский блок, а <structname> Middleware</structname> &quot;доверяет&quot; клиенту, создавая сессию по одному только логину пользователя без пароля методом <code>LoginService.loginTrusted()</code>. Метод <code>loginTrusted()</code> требует выполнения следующих условия:<itemizedlist>
<para>Возможен вариант, когда пароль пользователя (точнее, хэш пароля) не хранится в базе данных, а проверяется внешними средствами, например, путем интеграции с <application>ActiveDirectory</application>. В этом случае фактически аутентификацию выполняет клиентский блок, а <structname> Middleware</structname> &quot;доверяет&quot; клиенту, создавая сессию по одному только логину пользователя без пароля методом <code>LoginService.loginTrusted()</code>. Метод <code>loginTrusted()</code> требует выполнения следующих условия:<itemizedlist>
<listitem>
<para>клиентский блок должен передать так называемый доверенный пароль, задаваемый на <structname>Middleware</structname> и на клиентском блоке свойством приложения <property>
<link linkend="cuba.trustedClientPassword">cuba.trustedClientPassword</link>
@ -2451,9 +2460,7 @@ CustomerGrade.STANDARD=Стандартный</programlisting></para>
<para>Вход в систему для процессов внутри <structname>Middleware</structname> выполняется вызовом <code>LoginWorker.loginSystem()</code> с передачей логина пользователя (без пароля), от имени которого будет работать данный процесс. В результате создается объект <code>
<link linkend="userSession">UserSession</link>
</code>, который будет закэширован в данном блоке <structname>Middleware</structname> и не будет реплицироваться в кластере. </para>
<para>Стандартная реализация входа / выхода для компонентов <structname>Middleware</structname> заключена в специальном базовом классе <code>
<link linkend="managementBean">ManagementBean</link>
</code>, поэтому свои JMX-бины рекомендуется наследовать от него.</para>
<para>Более подробно аутентификация процессов внутри Middleware рассмотрена в разделе <xref linkend="system_authentication"/></para>
</section>
<section id="securityContext">
<title>SecurityContext</title>
@ -3060,7 +3067,7 @@ public String foo(String value) {
</itemizedlist></para>
<para>Загрузка по требованию работает только для экземпляра в состоянии <link linkend="entity_states">Managed</link>, то есть внутри транзакции, загрузившей данный экземпляр.</para>
</section>
<section>
<section id="query">
<title>Выполнение JPQL запросов</title>
<para>Для выполнения <link linkend="jpql">JPQL</link> запросов предназначен интерфейс <code>Query</code>, ссылку на который который можно получить у текущего экземпляра <code>EntityManager</code> вызовом метода <code>createQuery()</code>. Если запрос предполагается использовать для извлечения сущностей, рекомендуется вызывать <code>createQuery()</code> с передачей типа результата, что приведет к созданию <code>TypedQuery</code>. </para>
<para>Методы <code>Query</code> в основном соответствуют методам стандартного интерфейса <ulink url="http://docs.oracle.com/javaee/5/api/javax/persistence/Query.html">
@ -3082,9 +3089,10 @@ query.setParameter(1, customer);</programlisting></para>
</listitem>
</itemizedlist></para>
<section>
<title>Поиск like без учета регистра</title>
<title>Поиск подстроки без учета регистра</title>
<para>Для удобного формирования условия поиска без учета регистра символов и по любой части строки можно использовать префикс <literal>(?i)</literal> имени параметра запроса, например:<programlisting>select c from sales$Customer c where c.name like :(?i)name</programlisting></para>
<para>В данном случае ORM переведет значение параметра <literal>name</literal> в нижний регистр и обрамит символами <literal>%</literal>, а затем в БД выполнит SQL с условием вида <literal> lower(C.NAME) like ?</literal></para>
<para>Следует иметь в виду, что при таком поиске индекс, созданный в БД по полю <code>NAME</code>, не используется.</para>
</section>
<section>
<title>Макросы в JPQL</title>
@ -8684,6 +8692,66 @@ protected boolean postCommit(boolean committed, boolean close) {
<para>если элемент расположен внутри элемента другого источника, создается <code>NestedDatasource</code>, при этом внешний источник становится его хозяином. </para>
</listitem>
</itemizedlist></para>
<para>Рассмотрим схему XML. </para>
<para><sgmltag>dsContext</sgmltag> - корневой элемент.</para>
<para>Элементы <sgmltag>dsContext</sgmltag>:<itemizedlist>
<listitem>
<para><sgmltag>datasource</sgmltag> - определяет источник данных, содержащий единственный экземпляр сущности. </para>
<para>Атрибуты:<itemizedlist>
<listitem>
<para><sgmltag>id</sgmltag> - идентификатор источника, должен быть уникальным в для данного <code>DsContext</code>.</para>
</listitem>
<listitem>
<para><sgmltag>class</sgmltag> - Java класс сущности, которая будет содержаться в данном источнике</para>
</listitem>
<listitem>
<para><sgmltag>view</sgmltag> - имя <link linkend="views">представления</link> сущности. Если источник сам загружает экземпляры, то это представление будет использовано при загрузке. В противном случае это представление сигнализирует внешним механизмам о том, как нужно загрузить сущность для данного источника.</para>
</listitem>
<listitem>
<para><sgmltag>allowCommit</sgmltag> - при установке значения <code>false</code> метод <code>isModified()</code> данного источника всегда возвращает <code>false</code>, а метод <code>commit()</code> ничего не делает. Таким образом, изменения содержащихся в источнике сущностей игнорируются. По умолчанию <code>true</code>, т.е. изменения отслеживаются и могут быть сохранены.</para>
</listitem>
<listitem>
<para><sgmltag>datasourceClass</sgmltag> - нестандартный класс реализации источника данных, если необходим.</para>
</listitem>
</itemizedlist></para>
</listitem>
<listitem>
<para><sgmltag>collectionDatasource</sgmltag> - определяет источник данных, содержащий коллекцию экземпляров.</para>
<para>Атрибуты <sgmltag>collectionDatasource</sgmltag>:<itemizedlist>
<listitem>
<para><sgmltag>refreshMode</sgmltag> - режим обновления источника, по умолчанию <code>ALWAYS</code>. В режиме <code>NEVER</code> при вызове <code>refresh()</code> источник не производит загрузку данных, а только переходит в состояние <code>Datasource.State.VALID</code>, оповещает слушателей и сортирует имеющиеся в нем экземпляры. Режим <code>NEVER</code> удобен, если необходимо программно заполнить <code>CollectionDatasource</code> предварительно загруженными или созданными сущностями. Например:<programlisting>@Override
public void init(Map&lt;String, Object&gt; params) {
Set&lt;Customer&gt; entities = (Set&lt;Customer&gt;) params.get(&quot;customers&quot;);
for (Customer entity : entities) {
customersDs.includeItem(entity);
}
customersDs.refresh();
}</programlisting></para>
</listitem>
<listitem>
<para><sgmltag>softDeletion</sgmltag> - значение <code>false</code> отключает режим <link linkend="soft_deletion">мягкого удаления</link> при загрузке сущностей, т.е. будут загружены также и удаленные экземпляры. По умолчанию <code>true</code>.</para>
</listitem>
</itemizedlist></para>
<para>Элементы <sgmltag>collectionDatasource</sgmltag>:<itemizedlist>
<listitem>
<para><sgmltag>query</sgmltag> - запрос для загрузки сущностей</para>
</listitem>
</itemizedlist></para>
</listitem>
<listitem>
<para><sgmltag>groupDatasource</sgmltag> - полностью аналогичен <sgmltag>collectionDatasource</sgmltag>, но создает реализацию источника данных, пригодную для использования совместно с компонентом <code>
<link linkend="gui_GroupTable">GroupTable</link>
</code>.</para>
</listitem>
<listitem>
<para><sgmltag>hierarchicalDatasource</sgmltag> - аналогичен <sgmltag>collectionDatasource</sgmltag>, и создает реализацию источника данных, пригодную для использования совместно с компонентами <code>
<link linkend="gui_Tree">Tree</link>
</code> и <code>
<link linkend="gui_TreeTable">TreeTable</link>
</code>.</para>
<para>Специфическим атрибутом является <sgmltag>hierarchyProperty</sgmltag>, задающий имя атрибута сущности, по которому строится иерархия.</para>
</listitem>
</itemizedlist></para>
<para>Все созданные декларативно источники данных регистрируются в объекте <code>DsContext</code> экрана. <code>DsContext</code> решает следующие задачи:<itemizedlist>
<listitem>
<para>Позволяет организовать зависимости между источниками данных, когда при навигации по одному источнику (т.е. при изменении &quot;текущего&quot; экземпляра методом <code>setItem()</code>) обновляется связанный источник. Такие зависимости дают возможность в экранах легко организовывать master-detail связи между компонентами.</para>
@ -8740,19 +8808,128 @@ select o.customer from sales$Order o /* неверно - тип from (Order) о
</section>
<section>
<title>Параметры запроса</title>
<para>JPQL запрос в источнике данных может содержать параметры нескольких видов. Вид параметра определяется по префиксу имени параметра. Префиксом является часть имени до знака &quot;$&quot;. <itemizedlist>
<listitem>
<para>Префикс <code>ds</code>. </para>
<para>Значением параметра являются данные другого источника данных, имя которого указано после символа &quot;$&quot;. Например:<programlisting>&lt;collectionDatasource id=&quot;customersDs&quot; class=&quot;com.sample.sales.entity.Customer&quot; view=&quot;_local&quot;&gt;
&lt;query&gt;select c from sales$Customer c&lt;/query&gt;
<para>JPQL запрос в источнике данных может содержать параметры нескольких видов. Вид параметра определяется по префиксу имени параметра. Префиксом является часть имени до знака &quot;$&quot;. Интерпретация имени после &quot;$&quot; рассматривается ниже.</para>
<itemizedlist>
<listitem>
<para>Префикс <code>ds</code>. </para>
<para>Значением параметра являются данные другого источника данных, зарегистрированного в этом же <code>DsContext</code>. Например:<programlisting>&lt;collectionDatasource id=&quot;customersDs&quot; class=&quot;com.sample.sales.entity.Customer&quot; view=&quot;_local&quot;&gt;
&lt;query&gt;
select c from sales$Customer c
&lt;/query&gt;
&lt;/collectionDatasource&gt;
&lt;collectionDatasource id=&quot;ordersDs&quot; class=&quot;com.sample.sales.entity.Order&quot; view=&quot;_local&quot;&gt;
&lt;query&gt;select o from sales$Order o where o.customer.id = :ds$customersDs&lt;/query&gt;
&lt;query&gt;
select o from sales$Order o where o.customer.id = :ds$customersDs
&lt;/query&gt;
&lt;/collectionDatasource&gt;</programlisting></para>
<para>В данном случае параметром запроса источника данных <code>ordersDs</code> будет текущий экземпляр сущности, находящийся в источнике данных <code>customersDs</code>. </para>
<para>В данном случае параметром запроса источника данных <code>ordersDs</code> будет текущий экземпляр сущности, находящийся в источнике данных <code>customersDs</code>. </para>
<para>При использовании параметров с префиксом <code>ds</code> между источниками данных автоматически создаются зависимости, приводящие к обновлению источника если меняется значение его параметра. В приведенном примере если изменяется выбранный Покупатель, автоматически обновляется список его Заказов. </para>
<para>Обратите внимание, что в примере запроса с параметром левой частью оператора сравнения является значение идентификатора <code>o.customer.id</code>, а правой - экземпляр <code>Customer</code>, содержащийся в источнике <code>customersDs</code>. Такое сравнение допустимо, так как при выполнении запроса на Middleware реализация интерфейса <link linkend="query">
<code>Query</code>
</link>, присваивая значения параметрам запроса, автоматически подставляет ID сущности вместо переданного экземпляра сущности.</para>
<para>В имени параметра после префикса и имени источника может быть также указан путь по графу сущностей к атрибуту, из которого нужно взять значение, например: <programlisting>&lt;query&gt;
select o from sales$Order o where o.customer.id = :ds$customersDs.id
&lt;/query&gt;</programlisting></para>
<para>или<programlisting>&lt;query&gt;
select o from sales$Order o where o.tagName = :ds$customersDs.group.tagName
&lt;/query&gt;</programlisting></para>
</listitem>
<listitem>
<para>Префикс <code>custom</code>. </para>
<para>Значение параметра будет взято из объекта <code>Map&lt;String, Object&gt;</code>, переданного в метод <code>refresh()</code> источника данных. Например:<programlisting>&lt;collectionDatasource id=&quot;ordersDs&quot; class=&quot;com.sample.sales.entity.Order&quot; view=&quot;_local&quot;&gt;
&lt;query&gt;
select o from sales$Order o where o.number = :custom$number
&lt;/query&gt;
&lt;/collectionDatasource&gt;</programlisting><programlisting>Map&lt;String, Object&gt; params = new HashMap&lt;&gt;();
params.put(&quot;number&quot;, &quot;1&quot;);
ordersDs.refresh(params);</programlisting></para>
<para>Приведение экземпляра при необходимости к его идентификатору осуществляется аналогично параметрам с префиксом <code>ds</code>. Путь по графу сущностей в имени параметра в данном случае не поддерживается. </para>
</listitem>
<listitem>
<para>Префикс <code>param</code>. </para>
<para>Значение параметра будет взято из объекта <code>Map&lt;String, Object&gt;</code>, переданного при открытии экрана в метод <code>init()</code> <link linkend="screen_controller">контроллера</link>. </para>
<para>Приведение экземпляра при необходимости к его идентификатору осуществляется аналогично параметрам с префиксом <code>ds</code>. Поддерживается путь к атрибуту по графу сущностей в имени параметра. </para>
</listitem>
<listitem>
<para>Префикс <code>component</code>. </para>
<para>Значением параметра будет текущее значение визуального компонента, путь к которому указан в имени параметра. Например:<programlisting>&lt;query&gt;
select o from sales$Order o where o.number = :component$filter.orderNumberField
&lt;/query&gt;</programlisting></para>
<para>Путь к компоненту должен включать все вложенные <link linkend="frame">фреймы</link>.
</para>
<para>Приведение экземпляра при необходимости к его идентификатору аналогично параметрам <code>ds</code>.
Поддерживается путь к атрибуту по графу сущностей в имени параметра как продолжение пути к компоненту. </para>
</listitem>
<listitem>
<para>Префикс <code>session</code>. </para>
<para>Значением параметра будет значение атрибута <link linkend="userSession">пользовательской сессии</link>, указанного в имени параметра.</para>
<para>Значение извлекается методом <code>UserSession.getAttribute()</code>, поэтому поддерживаются также предопределенные имена атрибутов сессии: <itemizedlist>
<listitem>
<para><code>userId</code> - ID текущего зарегистрированного или замещенного пользователя;</para>
</listitem>
<listitem>
<para><code>userLogin</code> - логин текущего зарегистрированного или замещенного пользователя в нижнем регистре.</para>
</listitem>
</itemizedlist></para>
<para>Пример:<programlisting>&lt;query&gt;
select o from sales$Order o where o.createdBy = :session$userLogin
&lt;/query&gt;</programlisting></para>
<para>Приведение экземпляра при необходимости к его идентификатору аналогично параметрам <code>ds</code>.
Путь по графу сущностей в имени параметра в данном случае не поддерживается. </para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Фильтр запроса</title>
<para>Запрос источника данных может быть модифицирован во время работы приложения, в зависимости от вводимых пользователем условий, что позволяет эффективно фильтровать данные на уровне выборки из БД.</para>
<para>Простейший способ обеспечения такой возможности - подключение к источнику данных специального визуального компонента <link linkend="gui_Filter">Filter</link>.</para>
<para>Если по какой-то причине применение универсального фильтра нежелательно, можно встроить в текст запроса специальную разметку на XML, позволяющую сформировать итоговый запрос в зависимости от значений, введенных пользователем в произвольные визуальные компоненты экрана.</para>
<para>В таком фильтре могут быть использованы следующие элементы:<itemizedlist>
<listitem>
<para><sgmltag>filter</sgmltag> - корневой элемент фильтра. Может непосредственно содержать только одно условие.<itemizedlist>
<listitem>
<para><sgmltag>and</sgmltag>, <sgmltag>or</sgmltag> - логические условия, могут содержать любое количество других условий и предложений. </para>
</listitem>
<listitem>
<para>предложение на JPQL, которое добавляется в секцию <code>where</code>. Содержит только текст и опционально атрибут <sgmltag>join</sgmltag>, значение которого будет добавлено в соответствующее место запроса, если добавляется данное предложение <code>where</code>. </para>
</listitem>
</itemizedlist></para>
</listitem>
</itemizedlist></para>
<para>Условия и предложения добавляются в итоговый запрос, только если присутствующие внутри них параметры получили значения, т.е. не равны <code>null</code>. </para>
<para>Пример:<programlisting>&lt;query&gt;
select distinct d from app$GeneralDoc d
&lt;filter&gt;
&lt;or&gt;
&lt;and&gt;
&lt;c join=&quot;, app$DocRole dr&quot;&gt;dr.doc.id = d.id and d.processState = :custom$state&lt;/c&gt;
&lt;c&gt;d.barCode like :component$barCodeFilterField&lt;/c&gt;
&lt;/and&gt;
&lt;c join=&quot;, app$DocRole dr&quot;&gt;dr.doc.id = d.id and dr.user.id = :custom$initiator&lt;/c&gt;
&lt;/or&gt;
&lt;/filter&gt;
&lt;/query&gt;</programlisting></para>
<para>В данном случае если в метод <code>refresh()</code> источника данных переданы параметры <code>state</code> и <code>initiator</code>, а в визуальном компоненте <code>barCodeFilterField</code> установлено некоторое значение, то итоговый запрос примет вид:<programlisting>select distinct d from app$GeneralDoc d, app$DocRole dr
where
(
(dr.doc.id = d.id and d.processState = :custom$state)
and
(d.barCode like :component$barCodeFilterField)
)
or
(dr.doc.id = d.id and dr.user.id = :custom$initiator)</programlisting></para>
<para>Если же, к примеру, компонент <code>barCodeFilterField</code> пуст, а в <code>refresh()</code> передан только параметр <code>initiator</code>, то запрос получится следующим:<programlisting>select distinct d from app$GeneralDoc d, app$DocRole dr
where
(dr.doc.id = d.id and dr.user.id = :custom$initiator)</programlisting></para>
</section>
<section>
<title>Поиск подстроки без учета регистра</title>
<para>В источниках данных можно использовать особенность выполнения JPQL запросов, описанную для интерфейса <code>
<link linkend="query">Query</link>
</code> уровня Middleware: для удобного формирования условия поиска без учета регистра символов и по любой части строки можно использовать префикс <code>(?i)</code> имени параметра запроса, например:<programlisting>select c from sales$Customer c where c.name like :(?i)component$customerNameField</programlisting></para>
<para>В данном случае значение параметра, взятое из компонента <code>customerNameField</code>, будет переведено в нижний регистр и обрамлено символами &quot;%&quot;, а затем в базе данных будет выполнен SQL запрос с условием вида <code>lower(C.NAME) like ?</code></para>
<para>Следует иметь в виду, что при таком поиске индекс, созданный в БД по полю <code>NAME</code>, не используется. </para>
</section>
</section>
</section>