# Класс
Определяет правила хранения и обработки таблицы базы данных.
Класс позволяет существенно ускорить разработку бизнес логики ориентированную на работу с данными.
Программисту достаточно объявить перечень атрибутов класса чтобы за счет кодо-генерации получить набор готовых сервисов.
Перечень генерируемых элементов:
- *Доменная автономная бизнес логика*(`Dpi`) \
Содержит код для автономной бизнес логики
- *Каркас прикладной автономной логики*(`Api`) \
`scala` класс с окончанием `Api`, в котором
пишется автономная бизнес логика для работы с классом.
Наследуется от `Dpi`
- *Доменная интерактивная бизнес логика*(`Dvi`)
- *Каркас прикладной интерактивной логики*(`Avi`) \
`scala` класс с окончанием `Avi`, в котором пишется
интерактивная бизнес логика.
Наследуется от `Dvi`
- *Доменная разметка выборки*(`dvm.xml`) \
Содержит сгенерированную по умолчанию разметку выборки.
- *Каркас прикладной декларации пользовательского интерфейса*(`Avm`) \
`xml` файл с расширением `avm.xml`, в котором пишется
разметка выборки
- Интеграция с `Orm`
- `Pojo` объект для хранения данных в кэше
- `Aro` объект интеграции pojo в фреймворк
```{note}
При кодо-генерации обычно создаются два элемента разных типов:
- `D` - Domain \
Доменный элемент всегда перезаписывается при кодо-генерации и содержит бизнес логику для
подключения сервисов.
- `A` - Application \
Прикладной элемент не изменяется при кодо-генерации и служит для
хранения бизнес логики написанной программистом вручную. Прикладной элемент наследуется от доменного элемента.
```
Доступные прикладные сервисы:
- Аудит \
Аудит предназначен для фиксации различных событий при работе
пользователей в системе
- Администрирование \
Позволяет управлять доступом к автономной и интерактивной бизнес логике за счет
выдачи прав на привилегии.
- Универсальная фильтрация \
Позволяет пользователю строить комплексную фильтрацию списка классов на уровне базы данных.
В универсальном фильтре можно использовать как поля самого класса и его коллекций так и поля классов
на которые есть ссылки.
- Авто нумерация \
Механизм выдачи номеров объектам класса, данный механизм позволяет переопределять алгоритм выдачи
номеров на проекте.
- Копирование объектов \
Кода генерация бизнес логики для копирование объектов
- Группировка \
Группировка используется для систематизации хранения объектов и удобства
восприятия пользователем, так же группировка позволяет массово управлять
характеристиками и настройками объектов класса.
- Сервис прикрепленных файлов \
Позволяет прикреплять к объектам класса
произвольные файлы
- Поиск по шаблону \
возможность поиска объектов класса по
частичному или полному совпадению введенного текса со значениями полей
объекта или его заголовка и мнемокода
- Объектные характеристики \
Возможность добавлять произвольные поля в класс на проекте
- Генерация штрих-кодов объекта
Механизм генерации штрих кодов объектам класса при создании
- Подписи объектов для печати \
Используется при печати отчетов. Позволяет формировать в печатной форме список лиц с
местом для подписи
- Полнотекстовый поиск \
Возможность класса осуществлять быстрый поиск по значению атрибутов
класса
## Схема окружения
Окружение класса создается в момент кода генерации:
![Окружение класса](../img/class_concept.jpg)
## Общие сведения о классах
Класс сущности(далее просто класс) определяет хранилище совокупности объектов(строк), имеющих одинаковые
характеристики, подчиняющихся общим настройкам и операциям,
функционирующих в рамках единой логики.
Класс содержит набор атрибутов, атрибут может быть:
- Простым значением \
Используется для ввода и хранения значения определенного типа данных
- Ссылочным \
Используется для выбора значения атрибута из справочника
или другого множества
- Переменной ссылочности \
Используется для ссылочности на любую таблицу в системе
- Ссылочным на класс \
Таблица классов является служебной таблицей мета данных
хранящей весь перечень классов в решении.
Класс должен иметь уникальное системное имя и наименование.
Правила наименования класса:
- системное имя должно задаваться на латинице
- Имя должно быть в формате `{Module}_{Name}` где:
- `Module` - Имя модуля
- `Name` - Имя класса \
Имя класса должно быть в единственном числе, именительном падеже
Пример:
- `Lbr_Book`
## Типы данных
В система имеет специализированный набот простых и объектных типов
для удобной обработки данных. Объектные типы при необходимости интегрированны
в контекст сессии что позволяет обеспечить высокую произоводительность
системы за счет минимизации операций сериализации\десириализации.
Основные типы данных, используемые для атрибутов класса:
- Целое число
- Дробное число
- Строка
- Дата
- Ссылка (на объект заданного класса)
- Ссылка на класс
- Переменная ссылка (на объект произвольного класса)
- Глобальный идентификатор gid
- Json контейнер
### Простые типы
Простыми типами являются: Число, строка, дата
| Тип | postgresql | odm | Рекомендация по использованию |
|-|-|-|-|
| Строка фиксированной длинны | varchar | Varchar | Текст до 4000 символов |
| Строка переменной длинны | text | Text | Текст до 15 мегабайт |
| Число | number | Number | Целое или дробное число |
| Дата | date | Date | Дата, дата и время |
### Ссылочные типы
Данные классов могут быть связаны между собой.
Для организации связей между классами используются специальные типы.
#### Ссылка и переменная ссылка
Ссылка на объект и переменная ссылка используются для организации
ссылочности объектов одного класса на объекты другого класса (или
объекты своего собственного класса).
Тип `ссылка` может хранить объекты
одного класса, который задан в настройках.
Тип `переменная ссылка`
хранит ссылку на объект любого класса.
#### Ссылка на класс
Хранит ссылку на класс. Обычно используется совместно с
типом `переменная ссылка` для хранения класса, объект которого
содержится в переменной ссылке.
#### Глобальный идентификатор gid
Gid является уникальным идентификатором в рамках системы. Переменная
ссылка на gid является альтернативой системе переменной ссылочности из
двух атрибутов (ссылка на класс + переменная ссылка на объект). Для
организации переменной ссылки через gid используется один атрибут.
##### Формат ссылки gid
Глобальный идентификатор состоит из строки:
```
gid :== idClass \ id[@ idNode]
```
Где:
- idClass – идентификатор класса \
Ссылка на объект в таблице btk_class
- id – идентификатор
- idNode – идентификатор удаленного нода \
В текущей базе, задается для
объекта созданного на удаленном узле (если используется механизм
репликации на уровне системы)
### Json контейнер
Json контейнер – это расширение объекта класса NoSQL нотацией в
реляционной СУБД. Контейнер не имеет жесткой, заранее определенной схемы
и основан на множестве пар «ключ‑значение». Это позволяет использовать
его как динамическое расширение объекта. Для добавления новых данных в
контейнер не требуется перекомпиляция кода или изменение структуры СУБД.
## Супертипы классов
Для проектирования существует несколько разновидностей класса, с помощью
которых можно реализовать модель с необходимой бизнес-логикой. Эти
разновидности класса называют супертипами.
Супертипы задают стандартное поведение для класса, а также задают
стандартную обвязку методами.
Основные супертипы:
- `reference` - Справочник \
Справочник — это прикладной объект, который позволяет хранить данные,
имеющие одинаковую структуру и списочный характер. Пример: Справочник
физ. Лиц; Места хранения; справочник ТМЦ.
- `document` - Документ \
Документ – это прикладной объект, который хранит данные о событиях или
операциях на предприятии. Пример: Заявка на отгрузку; Приходная
накладная; Акт сверки.
Документ обычно имеет атрибут состояние, который отражает его жизненный
цикл.
- `collection` - Коллекция \
Коллекции представляют собой классы, объекты которых не имеют права на
самостоятельное существование и могут быть созданы только для объектов
других классов. Коллекции применяются в качестве табличных частей
документов или логических развязок между классами.
Добавление коллекций в бизнес объект позволяет массово загружать
данные в объектный кэш, что минимизирует нагрузку на базу данных.
Так же возможно обход элементов коллекции по родителю без
транзакционного индекса, что уменьшает нагрузку на процессор.
- `vcollection` – Переменная коллекция \
Переменная коллекция расширяет возможности обычных коллекций и может
ссылается на родителя переменной ссылкой. Это требуется, когда для
нескольких классов используется одинаковая коллекция.
- `journal` - Журнал \
Журнал – это особый тип классов, приспособленный для хранения большого
количества записей. Такие классы имеют ограниченную функциональную
обвязку ядровыми методами фреймворка. Это позволяет увеличить
быстродействие при работе с журналом. Примеры: записи по потребности
ТМЦ на заказ в разрезе документов; журнал трудоемкости в разрезе
операций и т.д.
- `trait` – Трейт \
Абстрактный класс-предок, не имеющий собственной структуры хранения.
Такой класс содержит общую логику нескольких классов-потомков и
является частью механизма повторного использования кода.
- `mixin` – Миксин (класс-примесь) \
Миксин – это особый вид классов, которые служат для хранения данных из
разных классов. Используются для построения общих списочных форм
различных диалогов подбора в пользовательских интерфейсах, а также для
удобства обработки данных в прикладной бизнес-логике.
Миксин позволяет объединить несколько разных таблиц вместе что
дает возможность использовать внешние ключи и индексы на данное объединение.
## Бизнес-объект
Бизнес-объект (БО) - объединение нескольких классов и их коллекций в
группу для более удобного манипулирования ими при работе с кэшем и
конфигурировании вспомогательных сервисов.
Бизнес объект позволяет:
- Массово загружать данные в транзакционный кэш \
Для бизнес объекта можно указать стратегию загрузки данных
существенно уменьшающую количество запросов в базу данных.
Так как запросы пойдут не по каждому объекту а
по каждому классу бизнес объекта.
- Настраивать права доступа \
По бизнес объекту создается административный объект на котором
можно массово выдать привилегии для всех классов бизнес объекта
- Управлять электронной подписью \
Можно настроить правила подписи всего бизнес объекта включая
не только шапку но и все вложенные коллекции.
- Настраивать интеграцию и репликацию
### Навигация по бизнес-объекту
Навигацией является последовательное посещение элементов бизнес-объекта
сверху вниз. При навигации перемещение между объектами идет по кэшу, при
этом обеспечивается автоматическая дозагрузка объектов в кэш по
необходимости.
В процессе навигации объекты не блокируются и могут при необходимости
быть вытолкнуты из кэша, что вызовет автоматическую дозагрузку
(обновление).
Примеры навигации:
```scala
val empApi = EmployeeApi()
empApi.load(7452) :/ { id =>
println(id.id)
for(idDes <- AddressApi.byParent(id)){
println(s"address: city=${ idDes.get(_.city)}")
}
}
```
## Навигация в рабочем пространстве
Объекты загружаются из базы или из кросс – сессионного кэша, при
загрузке происходит пессимистическая либо оптимистическая блокировка.
Объекты находятся в рабочем пространстве до момента коммита. В момент
коммита рабочее пространство очищается.
При навигации можно производить модификацию объектов. Api гарантирует
корректную навигацию по мастер деталям без необходимости flush и clean
кэша или немедленной загрузки коллекций.
## Массовая загрузка объектов.
При массовой загрузке объектов. Происходит минимизация обращений к базе
данных. То есть при обходе в обычном режиме 3-х уровневого
бизнес-объекта произойдет n+2 запросов, где n количество деталей 2-го
уровня, 1 запрос, на запрос мастер объекта, 1 запрос на запрос коллекций
2-го уровня. Однако если объект запросить с помощью массового запроса,
то при его обходе произойдет всего 3 запроса. Что может ускорить
навигацию по объектам более чем в 10 раз.
Пример массового запроса:
```scala
for (rv <- new OQuery(Stk_WarrantAta.Type){
where (t.id in idap)
batchAll()
}){}
```
```{note}
Объектные запросы активно расходуют оперативную память. Это
накладывает ограничение на использование их в процедурах бизнес-логики.
Обычно объектные запросы используются для организации пользовательского
интерфейса (редактирование одного объекта с коллекциями. Справочники,
документы и т.д.), а для программирования внутренних процедур
бизнес-логики используются SQL запросы в БД.
```
## Работа c провайдерами строк
Провайдер строки - Rop используется для работы со строкой данных (Aro),
загруженных в рабочее пространство, обеспечивая гарантию того, что при
доступе к строке данная строка будет находиться в рабочем пространстве.
Метод получения rop:
```scala
thisApi().load(идентификатор.asNLong)
```
Примеры сеттеров в файле выборки:
```scala
val rop = thisRop
thisApi().setidContras(rop,getVar("super$id").asNLong)
```
Работа с rop в API:
```scala
for (ropGrade <- new OQuery(entityAta.Type){
where (t.idGdsGrade === idpGdsGrade)
}) {
setidGdsGrade(ropGrade, None.nl)
}
```
Работа с AnyRop(rop неизвестного типа):
```scala
anyRop match {
case Btk_GroupApi(ropGroup) =>
ropGroup.get(_.sCaption)
case Btk_ClassApi(ropClass) =>
ropClass.get(_.sName)
case _ => throw AppException("Ожидали роп группы или класса")
}
// получить из списка только ропы определенного класса
ropaAny
.collect {
case Btk_GroupApi(ropGroup) => ropGroup
}
```
## Оптимистическая блокировка
Так как система Global Postgres ориентирована на работу с короткими
транзакциями, фреймворк по умолчанию включает для классов
оптимистическую блокировку.
Принцип работы оптимистической блокировки:
- При загрузке строки в кэш, запоминается версия изменения
- Если строка изменяется, увеличивается версия изменения.
- При сохранении в базу происходит проверка того, что старая версия
изменения соответствует версии изменения в базе данных, если это не
так, то выдается ошибка применения изменений.
Для отключения оптимистической блокировки в классе необходимо указать
свойство:
```xml
```
```{note}
Для хранения версии изменений используется служебное
поле `nVersion_dz`
```
## Коллекции
Коллекцией является сущность, объекты которой не могут существовать без
ссылки на объект владелец. Классы коллекций объявляются в Odm
сущности-владельце.
В бизнес-объекте предполагается использование ленивых коллекций.
Элементы коллекции загружаются по необходимости, удаление добавление
происходит в фоновых коллекциях и не требует немедленного запроса к базе
данных.
Связывание сущностей владельца и коллекции
производится путём объявления элемента `collection` в секции
`collections`.
```xml
```
Для класса коллекции указывается супертип «collection», это гарантирует,
наследование scala-классов от необходимых системных классов.
```xml
```
### Формирование кода
При формировании кода сущности-владельца, производится пересоздание кода
всех коллекций. Dpi коллекции будет унаследован от ChildApi.
```scala
trait Xxx_XxxxDpi[T] extends ChildApi[java.lang.Long, ARO, API]
```
`Dvi` будет унаследован от `CollectionAvi`.
```scala
trait Xxx_XxxxDvi extends CollectionAvi
```
Основные методы для работы:
```scala
//создание по владельцу, возвращает rop созданного объекта
Api().insertByParent(ropMaster)
//загрузка всей коллекции по владельцу, возвращает обходчик записей отфильтрованных по rop предка
Api().byParent(ropMaster)
Api().byParent(idMaster)
//удаление по rop объекта
Api().delete(rop)
```
### Отображения-детали
Для формирования отображений выборки, данные которых ограничены по
значению ссылочного поля, необходимо в odm-файле, для соответствующего
ссылочного атрибута указать свойство genListCollectionRep="true". Будет
сформировано отображение List\_{attr}.
```xml
```
### Переменная ссылочность
Часто при проектировании бизнес-логики необходимо хранить в атрибуте
коллекции ссылки на объекты нескольких классов. В этом случае необходимо
хранить не только идентификатор ссылочного объекта, но и идентификатор
ссылочного класса.
Для реализации переменной ссылочности, в Оdm коллекции необходимо
создать атрибут способный ссылаться на поле «gid» мастер-объекта.
```xml
```
```{note}
У классов, на объекты которых может ссылаться коллекция
с переменной ссылочностью обязательно должен существовать атрибут «gid».
Рекомендуется индексировать данное поле.
```
### Каскадное удаление
Если у коллекции включено свойство «Каскадное удаление» (по умолчанию включено),
```xml
```
то в dpi мастера формируется вызов метода delete из коллекций.
Для подключенных `vcollection` (переменных коллекций) код удаления
необходимо писать вручную в api:
```scala
override def delete(rop: ApiRop): Unit = {
for (crop <- Bs_BankAccApi().byParent(rop)) {
Bs_BankAccApi().delete(crop)
}
for (crop <- Bs_DefSettlerAddressApi().byParent(rop)) {
Bs_DefSettlerAddressApi().delete(crop)
}
for (crop <- Bs_SettlerAddressApi().byParent(rop)) {
Bs_SettlerAddressApi().delete(crop)
}
super.delete(rop)
}
```
## Механизмы наследования
Во фреймворке нет полноценного классического наследования классов
сущностей. Понятие «наследование» используется в качестве описания
логической связи двух классов.
Вместо классического наследования фреймворк использует миксины и трейты.
### Миксин (Mixin)
Примесь (англ. Mix-in) — элемент языка программирования (обычно класс
или модуль), реализующий какое-либо чётко выделенное поведение.
Используется для уточнения поведения других классов, не предназначен для
порождения самостоятельно используемых объектов.
В системе Global3 Postgres миксин – это специализированный класс, каждый
объект (запись в таблице) которого соответствует одному объекту
подключенного класса (записи в таблице). Mixin-класс может иметь не
ограниченное число подключенных классов. Первичным ключом для
mixin-объектов является поле gidRef. Значение поля gidRef равно значению
поля gid объекта подключенного класса и заполняется в момент создания
объектов.
В Api миксина реализуются общие методы, которые могут использоваться для
всех подключенных классов.
Миксин может содержать произвольное число атрибутов. Атрибуты, имена
которых совпадают с именами атрибутов подключенных классов
(наследников), будут автоматически заполняться при установке значений в
атрибуты.
```{note}
Все scala-классы, соответствующие
миксину наследуются от системных классов D-ветви (имеют префикс «D»).
Остальные scala-классы, соответствующие обычным сущностям, наследуются
от системных классов S-ветви (имеют префикс «S»)
```
Для миксинов метод получения rop по `gidRef` называется `loadByGid`, а также
реализован метод аналогичный методу get для SApi
```scala
getByGid(gidpRef: NGid): Option[ApiRop]
```
#### Создание mixin-класса
В Odm файле необходимо объявить атрибут `gidRef`.
```xml
```
У класса указать свойство «supertype=”mixin”».
```xml
```
#### Формирование кода
При формировании кода для mixin-класса, Dpi будет унаследован от
`MixinApi`.
```scala
trait Xxx_XxxxDpi[T] extends MixinApi[java.lang.Long, ARO, API]
```
Dvi будет унаследован от `AppMixinAvi`.
```scala
trait Xxx_XxxDvi extends AppMixinAvi
```
Основные методы для работы:
```scala
//создание по rop мастера, возвращает rop миксина
Api().insertByParent(ropMaster)
//загрузка миксина по gidRef, возвращает rop миксина
Api().loadByGid(gidRef)
//удаления по gidRef
Api().deleteByKey(gidRef)
```
#### Объявление подключенных классов (классов-наследников)
Для связывания класса с миксином необходимо в Odm файле указать имена
классов-миксинов.
```xml
```
##### Формирование кода
При формировании кода сущности в Dpi будет добавлен код:
- Вставки миксин-объекта в методе `insert`
- Удаления миксин-объекта в методе `delete`
- Установки значений атрибутов миксина \
Код будет добавлен в сеттера атрибутов имена которых совпадают с
именами атрибутов сущности, в сеттерах атрибутов сущности.
При необходимости управления генерацией миксина вручную необходимо
отключить формирование кода в Dpi с помощью настройки `isDpiManaged`.
По умолчанию по всем подключённым миксинам генерируется код в Dpi
#### Системные миксины
Функционально не отличаются от прикладных миксинов.
Основной системный миксин это `Btk_Object`.
При формировании кода для классов с `supertype="document"` и
`supertype="reference"`, этот миксин автоматически подключается к классу.
Допускается отключение миксина Btk_Object путём указания свойства в Odm
файле:
```xml
```
Пример смотрите в классе `Btk_Group`
#### Ссылочность на миксин
Для атрибутов, ссылочных на миксин, необходимо указывать тип
`refAnyObject`:
```xml
```
Для возможности автоматической генерации HL и MC атрибутов, сеттеров
этих атрибутов и добавления их в `selectStatement` и `onRefreshExt`
необходимо указать в настройке `ref.class` класс миксина для ссылки.
```{important}
Для миксинов по ссылочным атрибутам не генерируются внешние
ключи. Вместо этого генерируются индексы.
```
### Трейт (Trait)
`Trait` — это механизм обеспечения повторного использования кода между
классами. В отличии от наследования класс может содержать несколько
трейтов.
Трейт не имеет объектов, собственной структуры хранения данных
и визуального представления.
Создание трейта:
1. Создайте Оdm файл
2. укажите свойство `supertype="trait"`
При формировании кода будут созданы только Dpi и Api файлы. Dpi будет
содержать только объявления сеттеров и геттеров.
#### Наследование сущности от трейта
Для наследования сущности от трейта, добавьте в Odm свойство класса
`with="Trait_Name"`
Сущность, наследуемая от трейта, должна иметь все атрибуты, объявленные
в трейте.
#### Перекрытие и порядок вызовов методов
Рассмотрим код трейта и наследуемого от него класса:
```scala
trait Gs3_TraitApi[T] extends Gs3_TraitDpi[T] {
override def setFNumber(rop: ApiRop, value: NNumber): Unit = {
Logger.Factory.get(getClass).info("Gs3_TraitApi.setFNumber")
super.setFNumber(rop, value)
}
}
class Gs3_DescendantApi extends Gs3_DescendantDpi[T] with Gs3_TraitApi[T] {
override def setFNumber(rop: ApiRop, value: NNumber): Unit = {
Logger.Factory.get(getClass).info("Gs3_DescendantApi.setFNumber")
super.setFNumber(rop, value)
}
}
```
В результате выполнения метода `Gs3_DescendantApi.setFNumber()`
последовательность вызовов будет следующей:
1. `Gs3_DescendantApi.setFNumber`
2. `Gs3_TraitApi.setFNumber`
3. `Gs3_DescendantDpi.setFNumber`
## Пример разметки класса
```xml
```