Справочные материалы#
Структура файла объектной модели#
Файл модели включает в себя XML-элементы, заданные с помощью тегов. При разметке файла модели необходимо использовать следующие правила:
Все элементы модели заключаются между парой тегов
<model>.Для каждого класса использовать теги
<class>...</class>.Для свойств классов использовать теги
<property/>(единые).Индексируемые свойства дублируются между парой тегов
<index>...</index>.
Пример разметки файла модели:
<model>
<class label="Международные реквизиты счета" name="InternationalRequisites">
<property label="Номер счета" name="number" type="String"/>
<property label="Международный номер счета" name="iban" type="String"/>
<index unique="false">
<property name="number"/>
</index>
</class>
</model>
Теги#
Модель#
Файл модель должен быть заключен между парой тегов <model></model>. Для этого тега предусмотрен ряд атрибутов.
Обязательный атрибут: model-name — название расширения модели (используется при генерации новых артефактов).
Необязательные атрибуты:
component-code— код фабрики в пространстве SberWorks.МЕТА;version— версия модели;table-prefix— назначение таблицам, индексам и т.п. фабричного префикса. По умолчанию — нет значения (пусто);versioned-entities— включение оптимистической блокировки сущностей для всех классов модели (по умолчанию — «false»);autoIdMethod— параметр назначения идентификаторов по умолчанию:SNOWFLAKEилиUUIDV4(по умолчанию — «SNOWFLAKE»).
Примечание
Оптимистическая блокировка не всегда ведет себя предсказуемо. Если принято решение о ее применении, необходимо дополнительно указать параметру плагина генерации модели
enableVersionedEntitiesзначение «true». Дополнительно можно ознакомиться с материалом об аннотации Version в JPA.
Внутри тега <model> предусмотрены следующие элементы:
Классы объектов. Процесс агрегации модели сходен с процессом построения деревьев.
Перечисления (тип Enum).
Интерфейсы — для обеспечения возможности построения union запросов.
Импорты.
Объявления внешних типов.
Класс#
Для классов предметной области используется пара тегов <class></class>, которая может содержать следующие атрибуты:
Обязательный атрибут —
name(имя класса).Необязательные атрибуты:
extends— имя расширяемого класса (см. описание в подразделе «Наследование классов»).label— пользовательское описание класса.is-abstract— объявление типа абстрактным (см. описание в подразделе «Наследование классов»). По умолчанию —false.strategy— стратегия наследования JPA-классов относительно физической реализации. По умолчанию — JOINED. Допустимо: SINGLE_TABLE.lockable— признак того, что к объекту применим функционал прикладных (пессимистических) блокировок. Допустим к установке только на базовых классах. Использование прикладных блокировок описано в разделе «Прикладные блокировки» в документе «Руководство прикладного разработчика».id-prefixed— стратегия генерации ID с заданным строковым префиксом (см. раздел «Стратегия генерации ID с заданным строковым префиксом» в документе «Руководство прикладного разработчика»).cloneable— признак того, что объект можно клонировать, по умолчанию —false. Более подробно с функцией клонирования можно ознакомиться в разделе «Команда создания иерархии объектов по образцу createBy» документа «Руководство прикладного разработчика».
К именам классов предъявляются следующие требования:
Имя класса должно начинаться с заглавной буквы. Символы — латинские. Длина — не более 40 символов CamelCase по умолчанию, но может быть изменена (см. раздел «Генерация артефактов серверного компонента» документа «Структура артефактов DataSpace»).
Зарезервированные имена классов: Status, Stakeholder, StatusGraph, BaseEntity.
Имена системных элементов БД зарезервированы. В случае совпадения имен свойств в теге
<property/>с зарезервированными именами, названия целевых колонок будут изменены. Например, если в model.xml определено свойство с именем «clob», то в БД под это свойство будет создана колонка «CLOB_». Полный список изменяемых имен можно посмотреть во вложенном файле reservedwords.xml.
Внутри тега <class></class> могут находиться следующие теги:
Теги свойств объектов.
Теги ссылок на внешние объекты.
Тег категории установки (определяет способ формирования id объектов).
Свойство#
Свойства объекта задаются с помощью тега <property/>.
Следующие атрибуты тега <property/> обязательные:
name— название свойства;type— тип свойства.
В качестве типов можно использовать примитивы DataSpace, специализированные типы, разработанные классы и перечисления.
К числу необязательных атрибутов относятся следующие позиции:
label— описание свойства.collection— тип коллекции. Допустимо: set (множество). По умолчанию считается, что свойство коллекцией не является.mappedBy— имя атрибута обратной ссылки (для создания связи OneToMany, OneToOne).parent— является дочерним по отношению к типу свойства. Данное свойство описывает дерево сущностей. Данное свойство ставится только на свойствах типа «ссылка из модели». По умолчаниюparent= «false».length— длина. Необязательная уточняющая настройка для типа String (по умолчанию — 254, максимальная длина — 4000, иначе — использовать поле типаCLOB) и BigDecimal (по умолчанию — 38). Для BigDecimal означает не масштабируемое значение.scale— масштаб. Уточняющая настройка для типа BigDecimal (по умолчанию — 10).mandatory— признак обязательности. По умолчанию — «false».index— признак индексирования свойства. По умолчанию — «false». Имеется возможность создавать комбинированные индексы (см. раздел «Добавление индекса»).unique— признак уникальности свойства. При значенииunique = false— индекс не уникален(значение по умолчанию), при значенииunique = true— индекс уникален. Необходимо учитывать разное поведение БД при наличии NULL-значений в составе поля индекса (возможна ошибка нарушения уникальности).default-value— значение по умолчанию.mask— Java-совместимое регулярное выражение проверки значения. Атрибут применим с типом String. Пример можно найти в файле объектной модели.
Идентификатор (Id)#
В DataSpace для идентификаторов объектов можно задать способ формирования id. Для этого необходимо использовать пару тегов <class></class> и указать способ назначения id объектов с помощью атрибута category (категория установки).
Доступны следующие стратегии формирования id:
SNOWFLAKE: Id задается через алгоритм snowflake. Передача пользовательского id запрещена. Является значением по умолчанию.UUIDV4: Id задается через алгоритм UUID v4. Передача пользовательского id запрещена.MANUAL: Id передается исключительно пользователем.AUTO_ON_EMPTY: Id может передаваться пользователем. Если пользователь не передал id, то id формируется алгоритмом snowflake.UUIDV4_ON_EMPTY: Id может передаваться пользователем. Если пользователь не передал id, то id формируется алгоритмом UUID v4.
Пример использования тега <id/>:
<class label="Базовый продукт клиента" name="Product">
<id category="MANUAL"/>
<property name="name" type="String" />
</class>
Атрибут category элемента класса id и атрибут модели autoIdMethod определяют способ формирования идентификаторов для связанных с этим классом служебных сущностей, таких как элементы коллекций внешних ссылок, истории статусов и т.п.
Если категория в классе не определена, то для служебных сущностей будет использована категория модели, т.е. — autoIdMethod.
В случае определения категории в классе (за исключением MANUAL) эта же категория будет использована для служебных сущностей независимо от значения autoIdMethod.
Обобщение способа формирования идентификаторов для связанных с классом служебных сущностей:
категория не определена или
MANUAL— используется значение атрибута моделиautoIdMethod;определена категорий
SNOWFLAKEилиAUTO_ON_EMPTY— используетсяSNOWFLAKE;определена категория
UUIDV4илиUUIDV4_ON_EMPTY— используетсяUUIDV4.
Генерация идентификатора алгоритмом SNOWFLAKE#
SNOWFLAKE — внутренний алгоритм, используется для обеспечения генерации уникального идентификатора в распределенной (децентрализованной) среде.
Генерируемый идентификатор представляет собой Long-представление восьмибайтового значения (Longs.fromByteArray(byte[])), определяемого по следующему алгоритму:
0-3 байты |
4 байт |
5 байт |
6-7 байты |
|---|---|---|---|
Монотонно возрастающая временная составляющая, 4 первых байта от |
Случайное число от -128 до 127. Можно определить фиксированное значение параметром |
Четвертый (по умолчанию) байт (октет) IPv4-адреса сетевого интерфейса. В случае наличия нескольких соответствующих IPv4-адресов происходит сортировка по данным адресам и выбор первого из них. Используемый номер октета можно определить параметром |
Последний и первый байт двухбайтового счетчика, если генерация нескольких значений в JVM идет в рамках одного временного интервала (первые 4 байта генерируемого значения) |
Примечание
Для уникальности генерации идентификаторов в распределенной системе (кластере) необходимо обеспечивать уникальность 4-го октета IPv4-адресов в рамках всего кластера экземпляров сервиса DataSpace.
Примечание
Для получения дополнительной информации о возможностях настройки алгоритма SNOWFLAKE рекомендуется ознакомиться с разделом «Выбор варианта формирования идентификаторов» документа «Руководство по системному администрированию».
Индекс#
Индексы в модели реализуются путем установки атрибута index на свойстве или описанием в отдельном теге <index> со ссылками на индексируемые свойства.
Примечание
Указание индекса на поле через тег
indexилиuniqueприводит к созданию индекса. Явное добавление индекса с этим полем приведет к дублированию.Дублирование индексов запрещено.
Порядок свойств в индексе имеет значение. Свойства должны строиться в последовательности от высокоселективного к низкоселективному.
Следующий фрагмент кода содержит пример комбинированного индекса:
<class label="Международные реквизиты счета" name="InternationalRequisites">
<property label="Номер счета" name="number" type="String"/>
<property label="Международный номер счета" name="iban" type="String"/>
<property label="SWIFT банка-получателя" name="swift" type="SwiftCodes"/>
<index unique="false">
<property name="number"/>
<property name="swift"/>
</index>
</class>
Изначально логика создания индексов следующая: если имеется несколько индексов, и среди них есть индексы, которые содержат одинаковые поля с одинаковой последовательностью, но отличаются количеством полей, то индексы с меньшим количеством полей являются избыточными, поскольку запрос в состоянии использовать индекс с большим количеством полей. Пример:
<class label="Международные реквизиты счета" name="InternationalRequisites">
<property label="Номер счета" name="number" type="String"/>
<property label="Международный номер счета" name="iban" type="String"/>
<property label="SWIFT банка-получателя" name="swift" type="SwiftCodes"/>
<index unique="false">
<property name="number"/>
<property name="swift"/>
</index>
<index>
<property name="number"/>
</index>
</class>
В примере выше присутствуют два индекса, при этом индекс с одним полем number — избыточен, поскольку имеется индекс с большим количеством полей number, swift, и соблюдается последовательность полей (первый — number). В этом случае индекс, состоящий только из поля number, создан не будет.
Если индекс с меньшим количеством полей уникальный, то он будет создаваться.
Примечание
Возможно отключить игнорирование избыточных индексов включением настройки плагина
generateAllIndices. При значенииtrueсоздадутся все индексы, определенные пользователем.
Индексирование коллекций внешних ссылок#
Для оптимизации выборки сущностей, коллекция внешних ссылок (reference) которых содержит ссылку на определенный объект, может понадобиться создание дополнительного индекса по идентификатору сущности в коллекции.
Индекс можно указать как на элементе reference через атрибут index или создав отдельный элемент индекса.
Индекс будет создан на вспомогательной служебной таблице, связывающей ссылки и таблицу владельца коллекции, по полю entityId.
Поля коллекции внешних ссылок (reference) не могут участвовать в составном индексе.
Пример:
<class name="SomeClass">
<reference name="clients" type="Client" collection="set"/>
<reference name="documents" type="Document" collection="set" index="true"/>
<index>
<property name="clients"/>
</index>
</class>
Разметка приведет к созданию индексов на столбец REFERENCE_ENTITYID для служебных таблиц T_RCISOMECLASSDOCUMENTS и T_RCISOMECLASSCLIENTS.
Индексирование коллекций примитивных значений#
Для оптимизации выборки сущностей с коллекцией примитивных значений может понадобиться создание дополнительного индекса по значению примитива в коллекции.
Индекс можно указать как на элементе property через атрибут index, так и создав отдельный элемент индекса.
Внимание!
Поля коллекции примитивных значений не могут участвовать в составном индексе.
Пример:
<class name="MyClass">
<property name="code" type="String"/>
<property name="aliases" type="String" collection="set"/>
<property name="names" type="String" collection="set" index="true"/>
<property name="values" type="String" collection="set" index="true" unique="true"/>
<index>
<property name="aliases"/>
</index>
</class>
Разметка приведет к созданию индексов на столбцы ALIASES, NAMES_, VALUES_ таблиц хранения значений.
Индексирование статусных полей#
Поскольку поле для хранения статусов задается неявно, то для создания индекса по статусу сущности необходимо явно определить индекс для сущности как отдельный элемент index.
В элемент необходимо добавить поле, хранящее соответствующий статус. Поле имеет имя statusFor<Имя наблюдателя>.
Пример:
<model model-name="products" version="DEV-SNAPSHOT">
<class name="Product">
<property name="code" type="String"/>
<index>
<property name="statusForService"/>
</index>
<index>
<property name="statusForService"/>
<property name="statusForUser"/>
</index>
<index>
<property name="statusForService"/>
<property name="statusForUser"/>
<property name="code"/>
</index>
</class>
<status-classes class="Product">
<stakeholder code="service" name="service"/>
<stakeholder code="user" name="user"/>
</status-classes>
<statuses class="Product">
<stakeholder-link code="service">
<status code="new" name="new" initial="true"/>
<status code="close" name="close"/>
</stakeholder-link>
<stakeholder-link code="user">
<status code="use" name="use" initial="true"/>
<status code="done" name="done"/>
</stakeholder-link>
</statuses>
</model>
Внешние ссылки#
Внешняя ссылка — ссылка на класс внешней системы или класс другого агрегата модели потребителя.
Типы внешних ссылок:
Ссылка на объект во внешней системе:

Типом ссылки является класс, не принадлежащий модели потребителя.
Ссылка на корень агрегата:

Типом ссылки является класс — корень агрегата
Ссылка на некорневую сущность агрегата:

Типом ссылки является класс подчиненный корню агрегата.
Все типы ссылок на внешние объекты задаются с помощью тега <reference/>.
Следующие атрибуты тега <reference/> обязательны:
name— название свойства.type— тип ссылки (класс из модели или внешней системы).
К необязательным атрибутам тега относятся следующие позиции:
label— описание свойства.collection— тип коллекции. Допустимо:set(множество). По умолчанию считается, что свойство коллекцией не является.integrity-check— логический атрибут, определяющий необходимость проверки целостности ссылки. Атрибут применим только с типами модели. Проверка выполняется при заполнении объекта, содержащего внешнюю ссылку. Оба объекта должны быть расположены в одном шарде. При установке параметра dataspace-core.integrity-check.delete=true дополнительно выполняется проверка при удалении объекта ссылки. Важно отметить, что контроль на удаление является дополнительной нагрузкой на модуль.
Примечание
Начиная с версии 1.16.0 Platform V DataSpace для исключения проблем, связанных с ошибками в именах типов, указываемых в reference, все типы, указываемые в модели для reference, должны быть либо объявлены в модели в качестве типа class (не embeddable и не abstract), либо в качестве external-type. Для таких ссылок merge-kind становится необязателен в отличие от внешних ссылок дедубликации (описание external-type для дедубликации см. в разделе Требования к модели для ReferenceUpdater).
Для сохранения обратной совместимости предоставляется возможность отключить строгий контроль типов для reference за счет указания в конфигурации maven-плагина генерации модели (model-api-generator-maven-plugin) параметра
disableStrictTypeCheck=true(по умолчанию = false, то есть строгий контроль включен).
Перечисления (enum)#
Перечисления (тип enum) можно использовать в качестве типы атрибута. Enum — набор зафиксированных статических переменных.
Перечисление можно расширить полями расширения.
Примечание
Все значения одного перечисления должны включать в себя единый набор расширений.
Пример типа enum показан в следующем фрагменте кода:
<enum name="Size">
<value name="S">
<extension name="rus" value="44"/>
<extension name="uk" value="small"/>
</value>
<value name="M">
<extension name="rus" value="46"/>
<extension name="uk" value="medium"/>
</value>
</enum>
Атрибуты тега <enum>:
name— имя класса-перечислителя (обязательное поле), в котором:символы — латинские;
длина не должна превышать 40 символов (по умолчанию, но может быть изменена, см. раздел «Генерация артефактов серверного компонента» документа «Структура артефактов DataSpace»);
наименование — в стиле CamelCase;
label— описание enum-класса.
Атрибуты тега <value>:
name— имя перечисляемого значения (обязательное поле), в котором:имя свойства полностью заглавными буквами;
длина — не более 40 символов (по умолчанию, но может быть изменена, см. раздел «Генерация артефактов серверного компонента» документа «Структура артефактов DataSpace»);
символы — латинские, цифры и нижнее подчеркивание;
label— краткое описание значения;description— полное описание значения.
Атрибуты тега <extension>:
name— имя расширения (обязательное поле);value— значение для расширения (обязательное поле, значение строковое).
Пример использования значений по умолчанию для enum показан во фрагменте кода ниже:
<enum name="size">
<value name="s"/>
<value name="m"/>
<value name="l"/>
<value name="xl"/>
</enum>
<class name="Product">
<property name="size" type="size" default-value="m"/>
...
</class>
Импорты#
Тег <import> позволяет менять конфигурацию ресурсов предметных моделей, задавать дополнительные опции их использования и импортировать ресурсы из сгенерированных Java-артефактов.
Тег необходимо включать в файл предметной модели (model.xml).
В примере ниже тег <import> подключает файл статусной модели с путем по умолчанию:
<model>
<import type="STATUS"/>
</model>
У тега имеется один обязательный атрибут: параметр «тип» (type="ТИП_ПАРАМЕТРА").
DataSpace поддерживает следующие типы импортируемых ресурсов предметной модели:
расширений модели;
статусной модели;
генератора словарей.
<import type="IMPORT"/>
Подробную информацию о способах дробления модели на файлы можно найти в разделе «Расширение модели».
<import type="STATUS"/>
Подробную информацию об использовании статусов можно найти в разделе «Управление статусами».
Если справочники расположены не в папке по умолчанию (model/dictionary), необходимо использовать выражение импорта с типом «DICTIONARY_GENERATOR» и путем к папке с файлами справочников.
<import type="DICTIONARY_GENERATOR" file="./dictionaryFolder"/>
Сведения о способах использования локальных справочников можно найти в разделе «Работа с локальными справочниками». В дополнение к импорту ресурсов предметной модели продукт поддерживает импорт технических ресурсов (например, библиотек репликации).
<import type="CLOUD-RELOCATION"/>
Структура файла статусов#
Файл с описанием статусов должен называться status.xml и должен находиться в той же папке, что и файл с описанием модели.
Файл должен использовать следующую иерархию тегов:
<status>
<status-classes>
<stakeholder>
<statuses>
<stakeholder-link>
<status>
<to>
Теги перечислены в таблице:
Tег |
Описание |
|---|---|
|
Корневой тег, без атрибутов |
|
Тег с вложенным элементом для каждого класса со статусами |
|
Тег для наблюдателя за статусами. Одно значения для каждого класса |
|
Тег группы статусов, одна единица на каждый класс со статусами |
|
Код наблюдателя в теге |
|
Тег для каждого из статусов рассматриваемого класса |
Таблица ниже содержит описание тегов и атрибутов:
Тег |
Атрибут |
Описание |
Обязательность (Y/N) |
|---|---|---|---|
|
class |
Имя класса, для которого необходимо определить статусы |
Y |
|
code |
Код наблюдателя |
Y |
|
name |
Имя наблюдателя |
Y |
|
class |
Имя класса, для которого необходимо определить статусы |
Y |
|
historical |
Признак хранения истории изменения статусов (см. раздел «О сохранении истории изменения статусов») |
N |
|
code |
Код наблюдателя в теге |
Y |
|
code |
Код статуса |
Y |
|
name |
Название статуса |
Y |
|
initial |
Признак начального статуса |
N |
|
— |
Тег для каждого возможного перехода |
— |
|
status |
Статус после перехода |
Y |
Пример файла со статусами объектов включен в состав документации.
Примечание
При использовании DataSpace Java SDK коды статусов используются в upperCase, так как реализованы в виде enum. Исходное значение статуса, указанное в статусной модели, можно получить путем вызова метода
getValue(). Например:ProductPartyPlatformStatus.PRODUCTCREATED.getValue(). С подробной инструкцией по использованию DataSpace Java SDK можно ознакомиться в документе «Руководство прикладного разработчика».При использовании протоколов GraphQL или JSON-RPC коды используются в том формате, в котором заданы в статусной модели.
Примечание
Атрибут
initialявляется условно-опциональным, поскольку определяется автоматически.
Вычисление начального статуса происходит по следующей схеме:
Найти все статусы для конкретного наблюдателя (stakeholder-link) в классе (statuses/class), на которые никто не ссылается.
Начальный статус должен быть один, в противном случае возникнет исключение. Тогда необходимо определить начальный статус (пометить нужный статус атрибутом
initial="true").
Указанный вручную или вычисленный автоматически статус с признаком initial будет устанавливаться, если при создании сущности не указать статус. В противном случае будет установлен переданный статус. Статусная библиотека без начального статуса не может работать.
Примитивные типы#
Таблица содержит список примитивных типов, и их соответствие примитивам языка Java и размерностям полей БД:
Тип в xml (model.xml) |
Параметры типа |
Коллекционный |
Тип в терминах Java |
Тип в Liquibase |
Тип в БД Oracle |
Тип в БД PostgreSQL |
Совместимые изменения |
|---|---|---|---|---|---|---|---|
String, string |
length — максимальная длина строки в байтах. По умолчанию — 254. Допустимые значения — от 1 до 4000 включительно |
v |
|
|
|
|
Увеличение length. Перевод в UnicodeString (при условии ограничения length в 2000) |
UnicodeString, unicodestring |
length — максимальная длина строки в символах. По умолчанию — 254. Допустимые значения — от 1 до 2000 включительно |
v |
|
|
|
|
Увеличение length. Перевод в String (при увеличении length минимум в 2 раза) |
Text, text |
x |
|
|
|
|
||
BigDecimal, bigdecimal, Decimal, decimal |
length — количество отводимых под число символов (значащих цифр, включая знаки после запятой, при этом десятичный разделитель и знак минуса не учитываются). Допустимые значения length — от 1 до 38. По умолчанию — 38. scale — количество знаков после запятой. Допустимые значения scale — от 0 до (length — 1). По умолчанию — 10 |
v |
|
|
|
|
Увеличение length, scale |
Integer, int, integer |
v |
|
|
|
|
||
Short, short |
v |
|
|
|
|
||
Long, long |
v |
|
|
|
|
||
Byte, byte |
v |
|
|
|
|
||
Boolean, bool, boolean |
x |
|
|
|
|
||
Character, char, character |
v |
|
|
|
|
||
Date, date |
v |
|
|
|
|
||
LocalDate, localDate |
v |
|
|
|
|
||
LocalDateTime, localDateTime |
length — точность, определяющая, сколько знаков после запятой должно сохраняться в секундах. По умолчанию — 3. Допустимое значение — от 0 до 6 включительно |
v |
|
|
|
|
Увеличение length. Перевод из Date |
OffsetDateTime, offsetDateTime |
length — точность, определяющая, сколько знаков после запятой должно сохраняться в секундах. По умолчанию — 3. Допустимое значение — от 0 до 6 включительно |
v |
|
|
|
|
|
Float, float |
v |
|
|
|
|
||
Double, double |
v |
|
|
|
|
||
Binary, binary, byte[] |
x |
|
|
|
|
||
Json, json |
x |
|
|
|
|
Примечание
Для типа OffsetDateTime время сохраняется в UTC для PostgreSQL и в локальной зоне для Oracle. Вычитывается время всегда в UTC.
Тип JSON
Примитивный тип json используется для задания ограничения на содержимое поля («текст, содержащий валидный json») и физического типа колонки в таблице БД. При работе с этим типом следует учитывать:
поля не могут быть коллекционными, иметь значение по умолчанию и ограниченный размер длины строки;
на уровне API поле является строкой содержащей json, т.е. текст
примердолжен быть представлен в виде"пример"", а на уровне протокола json-rpc/graphql необходимо использовать экранирование"\"пример\"";модуль
dataspace-coreпри выполнении пакета команд производит проверку значения поля на валидность формату json, что может быть лишним действием при работе с модулем только черезSDK. Проверку можно отключить параметромdataspace.types.normalizer.validateJson=false;для базы данных PostgreSQL используется тип данных
jsonb, который может трансформировать исходный json (например, может измениться порядок следования полей в объекте), что в свою очередь может отразиться на передаваемых в смежные системы векторах изменений. Если есть необходимость в соответствии значения в векторе изменений с последующим чтением из базы данных, необходимо установить параметрdataspace.types.normalizer.jsonbPrepareMode=PREPAREмодуляdataspace-core. По умолчанию параметр имеет значениеSKIP. Следует учитывать, что при значенииPREPAREвыполняется дополнительный запрос к базе данных;для работы со специфичными для базы данных функциями следует использовать пользовательские запросы;
по умолчанию использование типа
jsonотключено, для включения необходимо в плагинmodel-api-generator-maven-pluginв секциюconfigurationдобавить параметрjsonEnabledсо значениемtrue:<configuration> ... <jsonEnabled>true</jsonEnabled> </configuration>Также необходимо добавить параметр запуска для модуля dataspace-core (см. раздел «Параметры для сервиса dataSpace-core» документа «Руководство по установке»).
Значение свойств по умолчанию#
Чтобы не дублировать начальные значения при создании объекта, необходимо определять значения по умолчанию. Для этого необходимо указывать атрибут default-value. Поддерживаются значения по умолчанию для примитивов и enum-классов, как показано в таблице:
Тип примитива |
Допустимые значения |
Пример |
|---|---|---|
BigDecimal |
Рациональные числа (дроби не допускаются) |
|
Boolean |
|
|
Byte |
Целые числа |
|
Date |
В формате |
|
Double |
Рациональные числа (дроби не допускаются) |
|
Float |
Рациональные числа (дроби не допускаются) |
|
Integer |
Целые числа |
|
LocalDate |
now |
|
LocalDateTime |
now |
|
Long |
Целые числа |
|
OffsetDateTime |
now |
|
Short |
Целые числа |
|
String |
Набор символов |
|
Text |
|
Примечание
При вставке пустых строк в качестве значения по умолчанию серверы БД ведут себя по-разному. Поэтому вне зависимости от БД, DataSpace интерпретирует вставку пустой строки в качестве значения по умолчанию как null-величину.
Примечание
При использовании значения
nowдля значения по умолчанию дат, конечное значение даты и времени зависит от времени окружения, на котором развернуто приложениеdataspace-core.
Примечание
При чтении поля типа OffsetDateTime время возвращается в UTC-формате.
Специализация примитивных полей#
В DataSpace можно использовать специализированные типы, производные из стандартных примитивов DataSpace.
Для использования специализированных типов в model.txt необходимо описать блок <type-defs></type-defs>. Пользовательские новые типы необходимо использовать в значении поля type свойства (property) в классе (class).
Структура type-defs показана во фрагменте ниже:
<type-defs>
<type-def>
* name — имя производного типа (обязательно).
* type — оригинальный примитивный тип DataSpace (обязательно).
* length — длина (доступно для String и BigDecimal).
* scale — масштаб (доступно для BigDecimal).
Заданная длина и масштаб будет устанавливаться только в том случае, если пользователь явно не переопределил аналогичное свойство на классе. При этом:
Для типа
Stringимеется возможность задания свойстваlength. По умолчанию — 254.Для типа
BigDecimalпомимоlengthможно указыватьscale. По умолчаниюlength = 38,scale= 10.Остальные типы не поддерживают
lengthиscale.
Примечание
Для типа
BigDecimalальтернативой атрибутаscaleявляется устаревший атрибутprecision, который не следует использовать при описании модели.
Пример использования для типа String и BigDecimal:
<type-defs>
<type-def length="12" name="ShortString" type="String"/>
<type-def length="12" name="ShortBigDecimal" scale="2" type="BigDecimal"/>
<type-def name="Position" type="Boolean"/>
</type-defs>
<class label="продукт" name="Product">
<property name="string" type="String"/> <!-- в итоге будет длина 254-->
<property name="shString" type="ShortString"/> <!-- в итоге будет длина 12-->
<property length="44" name="shString2" type="ShortString"/> <!-- в итоге будет длина 44-->
<property name="bigDecimal" type="BigDecimal"/> <!-- в итоге будет длина 38 масштаб 10-->
<property length="7" name="shortBigDecimal" type="ShortBigDecimal"/> <!-- в итоге будет длина 7 масштаб 2-->
<!-- Другие свойства... -->
</class>
Валидация значений строковых свойств при помощи регулярных выражений#
Для того чтобы наложить ограничение на формат значений для конкретного свойства, необходимо задать для него атрибут mask.
В качестве значения для атрибута mask выступает строка, содержащая регулярное выражение, формат которого должен соответствовать
спецификации, определенной языком Java.
<class name="MaskTesting">
<property name="ipAddress" type="String" mask="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"/>
</class>
@Test
public void maskTest() {
Packet successPacket = new Packet();
successPacket.maskTesting.create(param ->
param.setIpAddress("192.168.0.1"));
org.assertj.core.api.Assertions.assertThatCode(() -> dataspaceCorePacketClient.execute(successPacket)).doesNotThrowAnyException();
Packet failedPacket = new Packet();
failedPacket.maskTesting.create(param ->
param.setIpAddress("192.2141.0.211"));
org.assertj.core.api.Assertions.assertThatCode(() -> dataspaceCorePacketClient.execute(failedPacket))
.isInstanceOf(MaskNotMatchException.class)
.hasMessageContaining(
"должно соответствовать \"^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$\"");
}
Типы классов#
Абстрактные классы#
С помощью DataSpace можно моделировать абстрактные классы. После обработки модели на основе модели абстрактного класса будет сформирован абстрактный класс Java, который может наследовать у других классов, и выступать предком, но не может инициализировать сущности.
Для добавления абстрактного класса в модель необходимо использовать тег <class></class> с признаком is-abstract="true". В примере ниже абстрактный класс AbstractDeposit наследует у класса Product и выступает предком класса Deposit.
<class name="Product">
</class>
<class name="AbstractDeposit" extends="Product" is-abstract="true">
</class>
<class name="Deposit" extends="AbstractDeposit">
</class>
Внимание!
Ссылки на абстрактные классы (типа
<property type="*AbstractClass*"/>) недопустимы! Ограничения связаны с невозможностью определения конкретной сущности на физическом уровне (абстрактный класс не может порождать сущности и таблицы баз данных).
Интерфейсы#
Java-интерфейсы применяются для добавления в SDK функциональности выборки в одном запросе несвязанных по модели классов (реализации union all запросов).
В интерфейс выносятся общие свойства классов модели, при этом не используются атрибуты точности. Атрибуты, указанные в интерфейсы могут и должны быть использованы для здания сортировки объединяемых выборок. Информация по построению запроса на основе интерфейса приведена в разделе «Объединение запросов к разным агрегатам, обладающим одним интерфейсом (merge)» документа «Руководство прикладного разработчика».
Для моделирования интерфейса необходимо использовать тег <interface></interface>, как показано в примере ниже:
<interface name="WithCodeAndName">
<property name="code" type="String"/>
<property name="name" type="String"/>
</interface>
Для реализации интерфейса в классах необходимо использовать атрибут implements:
<class name="Product" label="Базовый продукт клиента" lockable="true" implements="WithCodeAndName">
<property name="code" type="String" label="код"/>
<property name="name" type="String" label="имя"/>
...
</class>
В моделях имплементирующих классов нужно добавить все те же свойства, что и в интерфейсе.
Есть ограничения по свойствам интерфейса:
Нельзя использовать атрибут label.
Нельзя создать индекс.
У интерфейса не может быть parent.
Нельзя использовать ссылку на внешний объект reference.
Интерфейс может содержать списки collection только примитивных типов либо перечислений Enum.
Перечисления (enum)#
С помощью DataSpace можно моделировать перечисления (тип enum), включающие в себя список значений. Перечисления можно использовать для моделирования справочников и некоторых других структур Java.
После обработки модели на основе созданного перечисления будет сформировано перечисление Java.
Для создания перечисления необходимо использовать тег <enum></enum>, как показано в примере ниже. Внутри тега <enum> необходимо указать возможные значения параметра (теги <value/>).
Перечисленные значения можно расширить с помощью тега <extension/>.
<enum name="Size">
<value name="S">
<extension name="rus" value="44"/>
<extension name="uk" value="small"/>
</value>
<value name="M">
<extension name="rus" value="46"/>
<extension name="uk" value="medium"/>
</value>
</enum>
Финальные классы#
С помощью DataSpace можно создавать финальные классы. После обработки модели на основе созданного будет сформирован финальный класс Java.
Для моделирования финального класса необходимо использовать атрибут final-class, как показано в примере:
<class name="TestEntity" label="Тестовая сущность" final-class="true">
<!-- ... -->
</class>
DDD-агрегаты#
В первых версиях DataSpace единственным агрегатом выступал «Клиент». Сущность была жестко зафиксирована на уровне Корпоративной Модели Данных («клиентоцентричная» модель). Такой подход порождал ограничения на разделение бизнес-сущностей.
В новом поколении Платформы это ограничение снято, предоставляя пользователю возможность самостоятельно определять границы агрегатов разрабатываемой модели: выделение продукта клиента в отдельный агрегат и т.п.
Общие сведения о способах использования агрегатов можно найти в следующих англоязычных статьях на сторонних ресурсах:
DDD_Aggregate (Мартин Фаулер).
Aggregates in Domain Driven Design (Chen Chen, Medium).
Концепция разделения модели на агрегаты накладывает следующее ограничение: транзакция (оптимистические блокировки, выполнение пакета команд (UnitOfWork)) по умолчанию возможна только в рамках одного экземпляра агрегата. Имеется возможность разрешить исполнение транзакций, затрагивающих данные нескольких агрегатов. Описание правил транзакционности внутри пакета команд доступно в разделе «Транзакционная граница пакета» в документе «Руководство прикладного разработчика».
Правильное деление модели данных на агрегаты предоставляет следующие возможности:
Поддержка транзакции в рамках агрегата посредством оптимистичной блокировки (см. раздел «Использование оптимистических блокировок в пакетах команд» документа «Руководство прикладного разработчика»).
Поддержка согласованной репликации транзакций через Прикладной журнал: резервирование в StandIn, интеграция с КАП. Как следствие, возможность «быстрого» перехода на резервный контур достигается за счет обеспечения системных (скрытых от пользователя) блокировок на уровне агрегата. После «быстрого» переключения на резервный источник допускается, что не все транзакции на изменение по агрегату реплицированы. В то же время система не даст осуществить транзакцию за счет скрытой от пользователя системной блокировки, ожидая, пока произойдет синхронизация состояния, актуального тому, которое имело место на первичном контуре до начала перехода. Данный подход открывает большие возможности в достижении таких показателей системы, как «Надежность» (99.99…) и «Доступность» (24/7).
Возможность переноса групп объектов между шардами (зонами). Это в свою очередь предоставляет следующие возможности:
горизонтальное масштабирование: распределение БД на несколько независимых физических серверов и ребалансировка данных между ними;
возможность выделения части прикладных объектов (агрегатов) в зону greenfield, возврат в промышленную зону.
Разделение/слияние агрегатов, обусловленные требованиями логики проектируемого приложения, например:
дедубликация клиентов: слияние клиентских данных, обусловленное нахождением дублей клиентов для нормализации;
передача части бизнес-объектов от одного владельца другому (продажа продукта в связи с банкротством, факторинг и т.п.).
Условия при разметке#
При разметке агрегатов необходимо учитывать следующие обстоятельства:
Агрегаты строятся (связываются) на базовых классах (класс, который не имеет предка или вся цепочка классов-предков — абстрактные классы).
«Корень» должен иметь коллекцию «листьев» или ссылку на «объект-листы» с атрибутом
mappedBy(связь OneToMany или OneToOne соответственно).«Лист» должен иметь свойство, имя которого указано в mappedBy «корня».
На «листе» необходимо добавить атрибут
parent="true". Этот маркер добавит объект в дерево агрегата.Добавление связи наследования (
extends="<SomeClassName>") к агрегату порождает еще один агрегат типа дочернего класса. Классы агрегата дочернего класса не являются членам агрегата класса предка. Связь между таким классами необходимо формировать внешними ссылками.
Пример перехода от клиентоцентричной модели к продуктоцентричной#
В примере на рисунке ниже мы видим, что все сущности принадлежат одному Клиенту(555), но поле AggregateRoot заполняется на основе идентификатора Агрегирующей сущности. Каждый цвет — независимый агрегат, который может изменить клиента или находиться в другом шарде. Ссылки из агрегатов других сущностей могут быть лишь в виде однонаправленных SoftReference (как показано в примере ContractProduct -> ProductPart), или в виде однонаправленных ComplexSoftReference для возможности ссылаться на дочерний объект агрегата. При этом в данной ссылке указывается и корневая сущность агрегата (как показано на примере TariffList -> ProductRegister).

Связи OneToMany и ManyToOne#
Сущность 1 может иметь связь с неограниченным числом Сущностей 2, например, в случае связи Product: Client (1 — *).
Для обоих классов необходимо указать связанную сущность как параметр. В дополнение к этому, необходимо указать множественную связь: collection="set" mappedBy="{имяОбратнойСсылки}".
<class name="Product">
<property name="client" type="Client" parent="true"/>
</class>
<class name="Client">
<property name="products" type="Product"
collection="set" mappedBy="client"/>
</class>

Связь OneToOne#
Для обоих классов необходимо указать связанную сущность как параметр. Один из параметров необходимо указать как родительский (parent="true"), для второго необходимо указать обратную ссылку (mappedBy="client").
<class name="Product">
<property name="client" type="Client" parent="true"/>
</class>
<class name="Client">
<property name="product" type="Product"
mappedBy="client"/>
</class>

Пример центрирования агрегата#
Код ниже — пример задания отношений между сущностями. Код показывает, как и относительно каких классов возникает центрирование в обновленной версии DataSpace:
<class name="Product">
<property name="productLink" type="ProductLink" mappedBy="product"/>
<property name="service" type="Service" mappedBy="product"/>
</class>
<class name="ProductLink">
<property name="product" type="Product" parent="true"/>
</class>
<class name="Service">
<property name="product" type="Product" parent="true"/>
<property name="operation" type="Operation"
mappedBy="service"/>
</class>
<class name="Operation">
<property name="service" type="Service" parent="true"/>
</class>
<class name="Document">
<property name="subscriber" type="Subscriber"
mappedBy="document"/>
</class>
<class name="Subscriber">
<property name="document" type="Document" parent="true"/>
</class>
Диаграмма управления |
Центричность модели |
|---|---|
|
|
Пример клиентоцентричной модели агрегатов#
В коде ниже показан пример клиентоцентричной модели агрегата:
<class name="Client" label="клиент">
<property name="products" type="ProductParty" collection="set" mappedBy="client"
label="Коллекция продуктов"/>
<property name="contract" type="Contract" collection="set" mappedBy="client"
label="Коллекция контрактов"/>
</class>
<class name="Contract" label="Контракт">
<property name="client" type="Client" parent="true" label="клиент"/>
<property name="name" type="String" label="имя"/>
</class>
<class name="ProductParty" label="Базовый продукт клиента">
<property name="series" type="String" label="Серии"/>
<property name="client" type="Client" parent="true" label="клиент"/>
<property name="performedServices" type="PerformedService" collection="set"
mappedBy="product" label="Коллекция сервисов"/>
</class>
<class name="PerformedOperation" label="Фактическая операция">
<property name="service" type="PerformedService" parent="true" label="сервис"/>
<property name="name" type="String" label="имя"/>
</class>
<class name="PerformedService" label="Исполняемый сервис">
<property name="code" type="String" label="код"/>
<property name="performedOperations" type="PerformedOperation" collection="set"
mappedBy="service" label="операции"/>
<property name="product" type="ProductParty" label="продукт" parent="true"/>
<property name="states" type="String" collection="set" label="состояния"/>
</class>
Модель в виде дерева показана на изображении ниже:

Использование внешних ссылок#
При описании модели с учетом агрегатов могут возникнуть следующие вопросы:
Как использовать ссылку на сущность из других агрегатов?
Каким образом получить ссылку на объект из внешней системы?
Для решения этих вопросов реализован тег <reference>, который позволяет хранить только ссылку на объект. Свойство внешней ссылки является индексируемым.
Примечание
Если объект не является корнем в агрегатной модели, то внешняя ссылка состоит из двух параметров: ссылка на корень агрегата и ссылка на сам объект.
Вне зависимости от выбранного способа представления внешних ссылок для коллекций создается новая сущность с суффиксом Reference для хранения внешней ссылки.
Таким образом, существуют следующие способы описания внешних ссылок:
Базовая модель из двух агрегатов.
Ссылка на корень агрегата.
Ссылка на дочерний объект агрегата.
Ссылка на внешний объект.
Базовая модель из двух агрегатов#
Базовая модель включает в себя два агрегата:

Ссылка на корень агрегата#
Пример кода со ссылкой на корень агрегата содержится во фрагменте кода ниже:
...
<class name="Service">
<reference name="document" type="Document"/>
...
</class>
...

Ссылка на дочерний объект агрегата#
Пример ссылки на дочерний объект агрегата можно найти во фрагменте кода ниже:
...
<class name="Service">
<reference name="subscriber" type="Subscriber"/>
</class>

Ссылка на внешний объект#
Пример кода со ссылкой на внешний объект можно найти во фрагменте кода ниже:
...
<class name="Service">
<reference name="client" type="Client"/>
</class>
...


