# Класс Определяет правила хранения и обработки таблицы базы данных. Класс позволяет существенно ускорить разработку бизнес логики ориентированную на работу с данными. Программисту достаточно объявить перечень атрибутов класса чтобы за счет кодо-генерации получить набор готовых сервисов. Перечень генерируемых элементов: - *Доменная автономная бизнес логика*(`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 ```