Dbmm

  !   Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам)

M2M связь многие-к-многим традиционно декларируемая реляционно

CDbMappingTable
{
    x_keyObject1
    x_keyObject2
    x_keyLinkType
    x_other_link_attributes
}

имеет недостатки

  • СУБД не знает зачем это делается и не обеспечивает по умолчанию никакого автоматического контроля за такой связью
  • инкапсуляторы и ORM аналогично не контролируют правильность использования данного паттерна
  • первичные ключисамой связисовместимые с оффлайн-синхронизацией Bijouне обеспечиваются
  • нет никаких статических ограничений на типы ключей

Декларация таких связей этим хаком не выразимым реляционно напрямую приводит к неэффективности, отсутствию статического контроля в "лучшем" случае подмененного рантайм отказами и/или к копи пасте логики и деклараций.

Декларация CDb M2M связей

Поэтому связи, где композитным первичным ключем по сути являются внешние ключи двухдругих объектов БД уникально связанных заданным типом связи в Udb поддерживаются напрямую. В том числе и чтобы более эффективно воспользоваться средствами Bijouфундаментально транслирующих все данные в связи такого вида.

Связь декларируется CDb-классом наследованым от mmdbobject

class CDbPrjXxxmapping : public mmdbobject
{
    ...
};

Он может быть и пустой. Основные параметры связи наследуются от mmdbobject исключая их некорректную инициализацию. Только дополнительные свойства связи декларируются в этом классе как обычные проперти. Таким образом M2M связь имеет встроенную поддержку + гибкие возможности расширения обычными средствами Udb.

Регистрация связей в dbmm

Такие CDb классы можно размещать только в специальных маппинговых контейнерах dbmm<>. Они похожи на dblist но вместо добавления произвольных элементов доступ к MM связям осуществляется функцией

    ref<TYPE> dbmm::MapContainerToTrait(
            key keyMmTrait,
            key keyMmTie,
            ENetworkSync eNetworkSync = E_NetworkSync_Yes)

При вызове ее в форме

    ref<CDbMyMapping> rDbMapping =
        _x_rDbContainer->
            _x_dbmmMyMapping.Get().
                MapContainerToTrait(
                    keyTrait,
                    keyTie);

задаются все три необходимых и фиксированных ключа идентифицирующих связь:

  • первый объект, обычно основной берется из_x_rDbContainer
  • второй объект, обычно свойство или дополнительный аттачмент с содержимым в keyTrait
  • и тип связи - keyTie
Атрибуты ММ связи

От mmdbobject наследуются следующие встроенные атрибуты M2M связи:

  • 0)GetKey() - как и любой dbobject, только здесь он автоматически генерируемый (в MapContainerToTrait()) и устроен таким образом, что обеспечивается уникальность связи при сетевой синхронизации (приложению запрещено подразумевать знание природы устройства этого ключа, можно только быть увереным что он уникально идентифицирует единственную связь заданного типа между двумя объектами)
  • 1) GetDbContainerKey() - ключ первого объекта M2M связи
  • 2) GetMmTraitKey() - ключ второго объекта связи, очень часто это объект декларирующий свойство или поле, однако для некоторых типов связи это может быть объект того же типа что и первый
  • 3) GetMmTieKey() - тип данной связи: два объекта могут в данный момент времени иметь только одну связь данного типа
  • 4) x_sMmScalar/x_keyMmNonscalar - основное скалярное значение для типов связей такого требующих (распознается Bijou как значение проперти, не имеет специального смысла для Udb)
  • 5) Иные x-property объявленные в данном классе могут дополнительно описывать данную саязь
CommitRef

Обязательно применять при изменении 4) x_sMmScalar/x_keyMmNonscalar и дополнительных свойств связи как это делается с любым CDb объектом.

Удаление связей

Как любых других записей.

Поддержка в запросах Qx

Декларация MM CDb класса позволяет использовать его в Qx запросах точно так же как и любой другой класс. Свойства пропертей можно JOINить с другими таблицами и использовать с фильтрами.

В выражениях Qx можно непосредственно записывать вызовы функций GetMmTraitKey() и GetMmTieKey() и они (точно так же как вызовы GetKey/GetDbContainerKey для обычного dbobject) транслируются в корректный SQL.

Таким образом M2M связи представленные через dbmm ничего не теряют в совместимости с обычной реляционностью и не требуют никаких специальных хаков (к Bijou) мимо Udb.

Маппинг статических пропертей в dbmm с Bijou

Это единственная фича работающая для сабжа особым образом если применяется с Bijou и не имеющая эффекта для простого реляционного бакенда (как драйвера SQLite).

Если MapContainerToTrait() использующей системный TieProp в качестве keyTrait указан ключ, совпадающий со статически декларированным проперти CDb объекта контейнера то значения Scalar/Nonscalar будут указывать на одно и то же значение со статическим проперти.

Это дает возможность работать с именованными пропертями объектов как с обезличенными списками включающими и динамические проперти (добавляемые пользователем).

Естественно использование такого мапинга требует обязательной статической проверки связанности всех имен с ключами (GET_PROPERTY_NAME / ASSERT TBD). Рефакторинг статических деклараций никогда не должен приводить к неожиданным деградациям логики фактически использующей метаданные. Поэтому частью статических деклараций должно быть явное указание на их участие в динамических связях (TBD).

При этом если не добавлять в мапинг Trait совпадающий со статическими пропертями он сам там не появится. Это исключает какие бы то ни было случайные завязки логики на статические декларации.

Есть ли встроенное понятие NULL значений связей?

Нет.

Как нет отдельно различимого NULL значения у str, int и т.п. пропертей на которые связи могут маппироваться. Приложения могут объявлять свои x_bXxxValueAssigned флаги как параметры связи чтобы отличать неопределенные значения от пустых.

Контроль ключей

Тип первого/основного объекта ограничен классом контейнера в котором dbmm объявлен. Ограничения на keyTrait и keyTie - TBD

Ограничения

Нельзя декларировать несколько dbmm маппирующих даже в разных контейнерных классах множественные связи одного и того же типа для одного и того же первичного ключа. С точки зрения Bijou все записи во всех таблицах с одним первичным ключом образуют единый объект и декларации нескольких маппингов будут конфликтовать с неопределенным результатом (после выполнения CommitRef) если хранят связи одного типа.

Примеры M2M связей
Как значение проперти:
   Object --property--> Id = Scalar

как keyPatient->"Sex" = "Female"

  • 1) Object ==GetDbContainerKey()
  • 2) "Sex" == GetMmTraitKey()
  • 3) property == GetMmTieKey()
  • 4) "Female" == x_sMmScalar(x_keyMmNonscalar - unused)
  • 5) Иные x-property не требуются. Хотя например плугины Pex могли бы добавлять sbuf с со своими структурами к значению поля и это была бы дополнительная x_sbuf проперти данной связи.
Присоединение объектов как файлы аттачментов:
   Object --attach--> Attachment

как keyLetter --C_keyTieAttachFile--> keyFile

  • 1) keyLetter ==GetDbContainerKey()
  • 2) keyFile == GetMmTraitKey()
  • 3) C_keyTieAttachFile == GetMmTieKey()
  • 4) (x_sMmScalar- unused)(x_keyMmNonscalar - unused)
  • 5) Иные x-property не требуются если не требуется указать как именно данный File подсоединяется к Letter.
Иерархии
   keyTopic1 --add parent--> keyTopic2

Пропертей не имеет.

Классификации
   keySceenshot --class--> keyImageClass
   keyBug123 --category--> keyBugs

Часто попытки добавить дополнительные атрибуты сюда являются аномалиями чтобы избежать регистрации других независимых связей и могут нарушить нормализацию. Как например Bijou для компактности журнала считает keyContainer атрибутом Node --class--> ... ограничивая Node одним возможным контейнером.

Синонимы и см. так же
   Bug --synonym--> Defect
   Array --see also--> List

Особенность в том что оба объекта симметричны. Создание двух копий связи в обоих направлениях было бы ошибкой.

Статусы завершенности
   keyTask --in version--> Release_1.3.7 = "Beta"
   keyDevice --assembly--> keyAssemblyOperation = "Complete"

Может иметь проперти определяющие более сложные значения, ссылки, диапазоны. Может быть по сути частью более сложных тройственных связей. Значением связи может быть ключ объекта более полно описывающего связь если на него нужны ссылки и из других мест (обычная нормализация данных).

Когда использовать встроенные Scalar/Nonscalar

keyNonscalar автоматически маппируется Bijou на значения ключей в пропертях чтобы отличать их от строк. Все остальное - sScalar. Оба значения сразу заданы быть не могут.

Основное встроенное значение скаляра используется для эффективной и универсальной обработки в Bijou для основных, как правило читабельных пользователем, скалярных значений как

  • значений добавляемых полей как карточки пациента
  • значения расчетных параметров как динамически декларируемых индексов измерения
  • присоединенных пользователем свойств объектов как в Bics
  • свойств всех статических пропертей отмаппированых из CDb классов с включенным драйвером Bijou

В дополнительных свойствах связи могут содержаться

  • структурные данные невыразимые одним скаляром
  • бинарные данные, CPicture и проч не текстовое
  • флаги как bAssigned
  • ссылки на контекст в рамках которого данное свойство создано
  • идентификация прикладного типа значения если он динамический

В свойствах связи не могут содержаться данные по сути составляющие независимый объект БД требующий на себя внешние ссылки и цикл жизни которых может не совпадать с существованием связи, а так же дублирующиеся данные и не имеющие прямого отношения к связи а описывающие один из объектов в ней участвующих.

Неуникальные связи двух объектов

Классика:

   Аuthor --written--> Book

легко представима в CDbAuthor::_x_dbmmBooksWritten так как подразумевает что данный автор имеет только единственную связь типа "написал" c книгой.

Сложнее ситуация если мы захотим хранить

   Аuthor --edited--> Book

и таких правок одной и той же книги может быть много. Напрямую такая связь не предствима в dbmm и нам требуется введение дополнительного объекта CDbEdition:

   Аuthor --edited--> Edition
   + CDbEdition::x_keyBook

Книги же которые написал автор нужно получать динамически запросами к Edition этого автора.

Связи более чем двух объектов

Запрещено паразитировать на keyTie для обозначения третьего объекта! Он может быть только из набора декларированных типов связи. Тройственные и более уникальные и не уникальные связи и атрибуты этих связей требуют деклараций дополнительных CDb объектов по аналогии с "Аuthor --edited--> Edition" выше.

Какой объект первый какой второй

С точки зрения реляционной декларации казалось бы все равно

   Аuthor --writes--> Book
OR
   Book --written by--> Аuthor

Логика может работать и так и так. Для выбора первого/главного объекта нужно учитывать

  • каскадное удаление _x_dbmm при удалении контейнера
  • ограничение типа CDb контейнера
  • и тот факт что проперти, все присоединенное, все второчное обычно оказывается во втором объекте и свойства связи зависят от существования основного объекта.

В случае

 keyPatient->"Sex" = "Female"

ясно что значения живут вместе с пациентом и связи должны удаляться с ним. И

 keySexField --property of--> keyPatient = "Female"

практически абсурден. Даже если мы удалили поле карточки из конфига мы можем оставить уже заданные его значения для существующих пациентов. Первый вариант записи обеспечивает это.

В

   keyChildTask --add parent--> keyParentTask

или

   keyParentTask --parent of--> keyChildTask

вопрос ставится так: хотим ли мы чтобы при удалении дочерней задачи ссылки на нее продолжали висеть в родительских и при удалении родительских что должно оставаться в дочерних. Возможно мы и не захотим применять каскадное удаление и саязь будет жить сама по себе или удаляться с любым из двух объектов.

Связь

   keyLetter --attach--> keyFile

видимо верна в таком виде если письмо это контейнер и даже при удалении файла мы можем хотеть чтобы в письме осталась ссылка на то что файл там был. Для файла же исчезновение всех связей может означать что он мусор и подлежит уничтожению сам.

То есть выбор контейнера и Trait, теоретически симметричных, зависит от прикладной логики. Иногда - как для таблиц синонимов и "see also" первичного объекта в принципе нет.

Эта категория в данный момент пуста.