Giter Club home page Giter Club logo

entity's Introduction

entity - OneScript Persistence API

GitHub release GitHub license Статус Порога Качества Рейтинг Сопровождаемости

Библиотека Entity предназначена для работы с данными БД как с простыми OneScript объектами. Является реализацией концепции ORM и шаблонов DataMapper и ActiveRecord в OneScript. Вдохновение черпается из Java Persistence API и TypeORM.

Возможности:

  • описание таблиц БД в виде специальным образом аннотированных OneScript классов;
  • сохранение объектов OneScript в связанных таблицах БД;
  • поиск по таблицам с результатом в виде коллекции заполненных данными объектов OneScript;
  • абстрактный программный интерфейс (API), не зависящий от используемой СУБД;
  • референсная реализация полнофункционального коннектора к SQLite и PostgreSQL, а так же упрощенного коннектора к файлам JSON.

Описание публичного интерфейса - каталог docs.

Оглавление

Сущность - это обычный класс OneScript, размеченный служебными аннотациями. Обязательными аннотациями являются &Сущность и &Идентификатор.

Библиотека entity считывает состав аннотаций класса, строит модель данных и инициализирует таблицы базы данных для работы с объектами данного класса.

Ограничения:

  • класс-сущность должен иметь конструктор по умолчанию, либо конструктор без параметров, либо конструктор со значениями всех параметров по умолчанию.
// file: СтраныМира.os

// Данный класс содержит данные о странах мира.

&Идентификатор                        // Колонка для хранения ID сущности
Перем Код Экспорт;                    // Колонка по умолчанию имеет строковый тип

Перем Наименование Экспорт;           // Колонка `Наименование` будет создана в таблице, т.к. поле экспортное.

&Сущность                             // Объект с типом "СтраныМира" будет представлен в СУБД как таблица "СтраныМира"
Процедура ПриСозданииОбъекта()

КонецПроцедуры
// file: Документ.os

&Идентификатор
&ГенерируемоеЗначение                      // Заполняется автоматически при сохранении сущности
&Колонка(Тип = "Целое")                    // Хранит целочисленные значения
Перем Идентификатор Экспорт;               // Имя колонки в базе - `Идентификатор`

&Колонка
Перем Номер Экспорт;                       // Колонка `Номер` будет создана в таблице, т.к. поле экспортное

&Колонка
Перем Серия Экспорт;                       // Колонка `Номер` будет создана в таблице, т.к. поле экспортное

&Сущность(ИмяТаблицы = "Документы")
Процедура ПриСозданииОбъекта()             // Объект с типом "Документ" будет представлен в СУБД как таблица "Документы"

КонецПроцедуры
// file: ФизическоеЛицо.os

// Данный класс содержит информацию о физических лицах.

&Идентификатор                             // Колонка для хранения ID сущности
&ГенерируемоеЗначение                      // Заполняется автоматически при сохранении сущности
&Колонка(Тип = "Целое")                    // Хранит целочисленные значения
Перем Идентификатор Экспорт;               // Имя колонки в базе - `Идентификатор`

Перем Имя Экспорт;                         // Колонка `Имя` будет создана в таблице, т.к. поле экспортное.
&Колонка(Имя = "Отчество")                 // Поле `ВтороеИмя` в таблице будет представлено колонкой `Отчество`.
Перем ВтороеИмя Экспорт;

&Колонка(Тип = "Дата")                     // Колонка `ДатаРождения` хранит значения в формате дата-без-времени
Перем ДатаРождения Экспорт;

&Колонка(Тип = "Ссылка", ТипСсылки = "СтраныМира")
Перем Гражданство Экспорт;                 // Данная колонка будет хранить ссылку на класс `СтраныМира`

&ПодчиненнаяТаблица(Тип = "Массив", ТипЭлемента = "Документы", КаскадноеЧтение = Истина)
Перем Документы Экспорт;                   // Данное поле будет хранить массив ссылок на класс `Документ`.
                                           // Для хранения массива будет создана отдельная таблица.
                                           // Взведенный флаг "КаскадноеЧтение" сигнализирует о необходимости
                                           // инициализировать сущности в массиве при чтении объекта из СУБД.

&Сущность(ИмяТаблицы = "ФизическиеЛица")   // Объект с типом `ФизическоеЛицо` (по имени файла) будет представлен в СУБД в виде таблицы `ФизическиеЛица`
Процедура ПриСозданииОбъекта()

КонецПроцедуры
// Создание менеджера сущностей. Коннектором к базе выступает референсная реализация КоннекторSQLite.
// В качестве БД используется "база в оперативной памяти".
МенеджерСущностей = Новый МенеджерСущностей(Тип("КоннекторSQLite"), "FullUri=file::memory:?cache=shared");

// Создание или обновление таблиц в БД.
МенеджерСущностей.ДобавитьКлассВМодель(Тип("СтраныМира"));
МенеджерСущностей.ДобавитьКлассВМодель(Тип("Документ"));
МенеджерСущностей.ДобавитьКлассВМодель(Тип("ФизическоеЛицо"));

// После заполнения модели менеджер необходимо проинициализировать.
МенеджерСущностей.Инициализировать();

// Работа с обычными объектом OneScript.
СохраняемоеФизЛицо = Новый ФизическоеЛицо;
СохраняемоеФизЛицо.Имя = "Иван";
СохраняемоеФизЛицо.ВтороеИмя = "Иванович";
СохраняемоеФизЛицо.ДатаРождения = Дата(1990, 01, 01);

СтранаМира = Новый СтраныМира;
СтранаМира.Код = "643";
СтранаМира.Наименование = "Российская Федерация";

Паспорт = Новый Документ;
Паспорт.Номер = "11 11";
Паспорт.Серия = "111000";

// Присваиваем колонке с типом "Ссылка" конкретный объект с типом "СтраныМира"
СохраняемоеФизЛицо.Гражданство = СтранаМира;

// Инициализируем массив для хранения документов.
// Это можно сделать и в методе ПриСозданииОбъекта в классе ФизическоеЛицо.os
СохраняемоеФизЛицо.Документы = Новый Массив;
// Добавляем новый документ
СохраняемоеФизЛицо.Документы.Добавить(Паспорт);

// Сохранение объектов в БД
// Сначала сохраняются подчиненные сущности, потом высокоуровневые
МенеджерСущностей.Сохранить(СтранаМира);
МенеджерСущностей.Сохранить(Паспорт);
МенеджерСущностей.Сохранить(СохраняемоеФизЛицо);

// После сохранения СохраняемоеФизЛицо.Идентификатор содержит автосгенерированный идентификатор.
// Колонка "Гражданство" в СУБД будет хранить идентификатор объекта СтранаМира - значение "643".
// Для хранения документов будет создана отдельная таблица, в которой будут сохранены
// значения массива с привязкой к физическому лицу.

Для поиска сущностей существуют методы Получить() и ПолучитьОдно().

Метод Получить() возвращает массив найденных сущностей.

Метод ПолучитьОдно() возвращает одну (первую попавшуюся) сущность или Неопределено, если найти сущность не удалось.

Оба метода в качестве второго параметра могут принимать в себя условия отбора в следующих видах:

  • Неопределено (параметр не заполнен) - поиск без отборов;
  • Соответствие - пары ИмяПоля-ЗначениеПоля, используемые как отбор по "равно";
  • ЭлементОтбора - объект типа "ЭлементОтбора", позволяющий использовать более сложные условия, например, с видом сравнения "БольшеИлиРавно";
  • Массив - массив с элементами типа "ЭлементОтбора", позволяющий использовать сложные условия отбора, соединяемые через логическое И.

Поиск сущностей с простыми отборами

// Для поиска нескольких сущностей, удовлетворяющих условию, можно использовать метод Получить()
// При вызове метода без параметров будут полученные все сущности указанного типа.
// В массиве содержатся объекты типа "ФизическоеЛицо" с заполненными значениями полей.
// Поле "Гражданство" заполнится готовым объектом с типом "СтраныМира".
// Т.к. над полем "Документы" взведен флаг "КаскадноеЧтение", то данное поле
// заполнится массивом с готовыми объектами типа "Документ".
// В обратном случае в массиве содержались бы идентификаторы (ключи) сущностей "Документ".
НайденныеФизЛица = МенеджерСущностей.Получить(Тип("ФизическоеЛицо"));

// В метод Получить() можно передать отбор в виде соответствия
Отбор = Новый Соответствие;
Отбор.Вставить("Имя", "Иван");
Отбор.Вставить("ВтороеИмя", "Иванович");

// В результирующем массиве окажутся все "Иваны Ивановичи", сохраненные в БД.
НайденныеИваныИванычи = МенеджерСущностей.Получить(Тип("ФизическоеЛицо"), Отбор);

// Допустим в БД сохранено физ. лицо с идентификатором, равным 123.
// Для получения одной (первой попавшейся) сущности можно использовать метод ПолучитьОдно()
СохраненноеФизЛицо = МенеджерСущностей.ПолучитьОдно(Тип("ФизическоеЛицо"));

// В метод можно передать отбор в виде соответствия, аналогично методу Получить()
СохраненноеФизЛицо = МенеджерСущностей.ПолучитьОдно(Тип("ФизическоеЛицо"), Отбор);

// Если вызвать метод "ПолучитьОдно" с параметром не-соответствием, то будет осуществлен поиск по идентификатору сущности.
Идентификатор = 123;
СохраненноеФизЛицо = МенеджерСущностей.ПолучитьОдно(Тип("ФизическоеЛицо"), Идентификатор);
// Найдем всех физических лиц, у которых дата рождения больше, чем 01.01.1990.
ЭлементОтбора = Новый ЭлементОтбора("ДатаРождения", ВидСравнения.БольшеИлиРавно, Дата(1990, 1, 1));
НайденныеФизЛица = МенеджерСущностей.Получить(Тип("ФизическоеЛицо"), ЭлементОтбора);

// Найдем всех физических лиц, рожденных в 90-ые.
МассивОтборов = Новый Массив;
МассивОтборов.Добавить(Новый ЭлементОтбора("ДатаРождения", ВидСравнения.БольшеИлиРавно, Дата(1990, 1, 1)));
МассивОтборов.Добавить(Новый ЭлементОтбора("ДатаРождения", ВидСравнения.Меньше, Дата(2000, 1, 1)));

ДетиДевяностых = МенеджерСущностей.Получить(Тип("ФизическоеЛицо"), МассивОтборов);
// Допустим имеется сущность, которую надо удалить.

МенеджерСущностей.Удалить(СущностьФизическоеЛицо);

// После выполнения метода в БД не останется строки с идентификатором, равным идентификатору сущности

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

// Получение хранилища сущностей
ХранилищеФизЛиц = МенеджерСущностей.ПолучитьХранилищеСущностей(Тип("ФизическоеЛицо"));

// Поиск сущностей
Идентификатор = 1;
ФизЛицо = ХранилищеФизЛиц.ПолучитьОдно(Идентификатор);

ФизЛицо.Имя = "Петр";

ХранилищеФизЛиц.Сохранить(ФизЛицо);

Для упрощения работы с сущностями помимо сохранения и удаления сущностей через МенеджерСущностей или ХранилищеСущностей сами объекты сущностей декорируются дополнительными методами Сохранить, Прочитать и Удалить. Все типы сущностей, полученные из Менеджера или Хранилища сущностей с помощью методов Получить или ПолучитьОдно автоматически декорируются. Для создания нового экземпляра сущности, имеющего дополнительные методы, можно воспользоваться методом СоздатьЭлемент у Менеджера или Хранилища сущностей.

// Получение хранилища сущностей.
ХранилищеФизЛиц = МенеджерСущностей.ПолучитьХранилищеСущностей(Тип("ФизическоеЛицо"));

// Создание сущности, обладающей методами "активной записи".
ФизЛицо = ХранилищеФизЛиц.СоздатьЭлемент();
ФизЛицо.Идентификатор = 1;

// Чтение данных сущности по текущему идентификатору.
// Все поля сущности проинициализируются значениями из базы.
ФизЛицо.Прочитать();

// Изменение данных и сохранение через "активную запись"
ФизЛицо.Имя = "Петр";
ФизЛицо.Сохранить();

// Сущности, полученные из Хранилища сущностей сразу становятся "активной записью"
Идентификатор = 2;
ВтороеФизЛицо = ХранилищеФизЛиц.ПолучитьОдно(Идентификатор);

// И могут быть удалены, через методы "активной записи"
ВтороеФизЛицо.Удалить();

Методы по работе с транзакциями есть как в Менеджере сущностей, так и в Хранилище сущностей.

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

МенеджерСущностей.НачатьТранзакцию();

// Объекты ФизическоеЛицо и СтранаМира из примеров выше:
МенеджерСущностей.Сохранить(СтранаМира);
МенеджерСущностей.Сохранить(СохраняемоеФизЛицо);

МенеджерСущностей.ЗафиксироватьТранзакцию();

Для связями между классом на OneScript и таблицей в БД используется система аннотаций. Часть аннотаций обязательная к применению. Все параметры аннотаций необязательные.

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

Применение: обязательно

Каждый класс, подключаемый к менеджеру сущностей должен иметь аннотацию Сущность, расположенную над любым методом класса.

При отсутствии у класса методов рекомендуется навешивать аннотацию над методом ПриСозданииОбъекта().

Аннотация Сущность имеет следующие параметры:

  • ИмяТаблицы - Строка - Имя таблицы, используемой коннектором к СУБД при работе с сущностью. Значение по умолчанию - строковое представление имени типа сценария. При подключении сценариев стандартным загрузчиком библиотек совпадает с именем файла.

Применение: обязательно

Каждый класс, подключаемый к менеджеру сущностей должен иметь поле для хранения идентификатора объекта в СУБД - первичного ключа. Для формирования автоинкрементного первичного ключа можно воспользоваться дополнительной аннотацией ГенерируемоеЗначение.

Аннотация Идентификатор не имеет параметров.

Применение: необязательно

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

Референсная реализация коннектора на базе SQLite поддерживает единственный тип генератора значений - AUTOINCREMENT.

Планируется расширение аннотации указанием параметров генератора.

Аннотация ГенерируемоеЗначение не имеет параметров.

Применение: необязательно

Все экспортные поля класса (за исключением полей, помеченных аннотаций ПодчиненнаяТаблица) преобразуются в колонки таблицы в СУБД. Аннотация Колонка позволяет тонко настроить параметры колонки таблицы.

Аннотация Колонка имеет следующие параметры:

  • Имя - Строка - Имя колонки, используемой коннектором к СУБД при работе с сущностью. Значение по умолчанию - имя свойства.
  • Тип - ТипыКолонок - Тип колонки, используемой для хранения идентификатора. Значение по умолчанию - ТипыКолонок.Строка. Доступные типы колонок:
    • Целое
    • Дробное
    • Булево
    • Строка
    • Дата
    • Время
    • ДатаВремя
    • Ссылка
    • ДвоичныеДанные
  • ТипСсылки - Строка - Имя зарегистрированного в модели типа, в который преобразуется значение из колонки. Имеет смысл только в паре с параметром Тип, равным Ссылка. Допустимо указывать примитивные типы из перечисления ТипыКолонок и типы сущностей (например, "ФизическоеЛицо")

Применение: необязательно

Аннотация ПодчиненнаяТаблица используется для хранения коллекций - массивов и структур.

Аннотация ПодчиненнаяТаблица имеет следующие параметры:

  • ИмяТаблицы - Строка - Имя таблицы, используемой коннектором к СУБД при работе с сущностью. Значение по умолчанию - строка вида ИмяТаблицыСущности_ИмяСвойства.
  • Тип - ТипыПодчиненныхТаблиц - Тип колонки, используемой для хранения идентификатора. Доступные типы подчиненных таблиц:
    • Массив
    • Структура
  • ТипЭлемента - Строка - Имя зарегистрированного в модели типа, в который преобразуется значение из колонки. Допустимо указывать примитивные типы из перечисления ТипыКолонок и типы сущностей (например, "ФизическоеЛицо").
  • КаскадноеЧтение - Булево - Флаг, отвечающий за инициализацию сущностей в подчиненной таблице (если ТипЭлемента является ссылочным типом).

Описание публичного интерфейса - каталог docs.

МенеджерСущностей предоставляет публичный интерфейс по чтению, сохранению, удалению данных. МенеджерСущностей инициализируется конкретным типом коннектора к используемой базе данных. Все операции по изменению данных МенеджерСущностей делегирует Коннектору. В зоне ответственности МенеджераСущностей находятся:

  • Создание и наполнение МоделиДанных
  • Трансляция запросов от прикладной логики к коннекторам
  • Конструирование найденных сущностей по данным, возвращаемым коннекторами

ХранилищеСущностей предоставляет тот же интерфейс по работе с сущностями и транзакциями, но с глобальной привязкой к конкретному типу сущности. Для получения ХранилищаСущностей служит метод МенеджерСущностей::ПолучитьХранилищеСущностей.

В отличие от МенеджераСущностей, ХранилищеСущностей не требует передачи в методы параметра "ТипСущности".

Хранилища сущностей и пулы сущностей совпадают в рамках одного типа сущности, типа коннектора и строки соединения. Другими словами, два менеджера сущности, инициализированные одним и тем же коннектором и строкой соединения, вернут одинаковые хранилища сущностей одного типа.

Коннектор содержит в себе логику по работе с конкретной СУБД. Например, КоннекторSQLite служит для оперирования СУБД SQLite. В зоне ответственности коннектора находятся:

  • подключение к СУБД
  • работа с транзакциями
  • инициализация таблиц базы данных;
  • CRUD-операции над таблицами, в которых хранятся сущности (создание-получение-обновление-удаление);
  • преобразование типов по данным ОбъектаМодели в типы колонок СУБД.

Ко всем коннекторам предъявляются определенные требования:

  • каждый коннектор обязан реализовывать интерфейс, представленный в классе АбстрактныйКоннектор;
  • коннектор может писать предупреждающие сообщения или выдавать исключения на методах, которые он не поддерживает или поддерживает не полностью.

Например, КоннекторJSON не умеет работать с транзакциями, однако, он имеет соответствующие методы, выводящие диагностические сообщения при их вызове.

Важно!

Каждое ХранилищеСущностей и МенеджерСущностей хранят в себе отдельные экземпляры Коннекторов. Тип, строка соединения и параметры коннектора определяются при создании МенеджераСущностей.

Модель данных хранит в себе список всех зарегистрированных классов-сущности в виде ОбъектовМодели

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

  • тип сущности;
  • имя таблицы для хранения сущности;
  • список всех колонок, с информацией о:
    • имени поля класса;
    • имени колонки в БД;
    • типе колонки в БД;
    • типе элемента (в случае ссылочного типа колонки);
    • значения флага "Идентификатор";
    • значения флага "ГенерируемоеЗначение";
  • список подчиненных таблиц с информацией о:
    • имени поля класса
    • имени таблицы в БД
    • типе подчиненной таблицы
    • типе элемента
  • ссылку на данные колонки-идентификатора.

Помимо мета-информации ОбъектМодели позволяет получать значения колонок таблицы на основании имен полей сущности (и наоборот), вычислять значение идентификатора сущности, выполнять приведение типов и установку значений полей сущности.

В состав библиотеки входит референсная реализация интерфейса коннектора в виде коннектора к СУБД SQLite. Реализация базируется на библиотеке sql, есть поддержка работы в OneScript.Web.

Коннектор SQLite поддерживает все CRUD-операции над сущностями, простой и сложный поиск, работу с транзакциями.

Внимание!

При использовании in-memory базы данных в моделях больше, чем с одним типом сущности, строка соединения должна выглядеть так: "FullUri=file::memory:?cache=shared"

В состав библиотеки входит референсная реализация интерфейса коннектора в виде коннектора к СУБД PostgreSQL. Реализация базируется на библиотеке sql, есть поддержка работы в OneScript.Web.

Коннектор PostgreSQL поддерживает все CRUD-операции над сущностями, простой и сложный поиск, работу с транзакциями.

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

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

Коннектор SQLite поддерживает все CRUD-операции над сущностями, простой и сложный поиск, но не поддерживает работу с транзакциями. При вызове операций по работе с транзакциями будут выданы исключения.

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

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

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

Коннектор поддерживает все CRUD-операции над сущностями, простой и сложный поиск, но не поддерживает работу с транзакциями. При вызове операций по работе с транзакциями будут выданы исключения.

Библиотека entity в целом следует концепции семантического версионирования со следующими изменениями в правилах нумерации версий:

  • первая цифра версии - Major.Entity - версия API Менеджера сущностей;
  • вторая цифра версии - Major.Connector - версия API Коннекторов;
  • третья цифра версии - Minor - новая функциональность в рамках мажорных версий;
  • четвертая цифра версии - Patch - исправление ошибок.

Таким образом:

  • прикладное ПО может быть уверено в сохранении обратной совместимости в рамках первой цифры версии;
  • коннекторы к СУБД могут быть уверены в сохранении обратной совместимости и требований по реализации API в рамках второй цифры версии, невзирая на значение первой цифры.

Под контроль и обязательство соблюдения обратной совместимости попадают:

To be continued...

entity's People

Contributors

absolemus avatar dependabot[bot] avatar dvakor avatar nivanchenko avatar nixel2007 avatar otymko avatar sfaqer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

entity's Issues

getRepository - МенеджерСущности, заточенный под конкретную сущность

Чтобы упростить, например, поиск сущностей за счет отсутствия необходимости передавать тип.
Другими словами,

public class EntityManager {
  ...
  getRepository(entityType: T): Repository<T>
  ...
}

public class Repository<T> {
  get(id: any): T[],
  get(filter: Map<string, any>): T[],
  get(filter: Filter[]): T[], 
  save(entity: T): void
}

Пулл соединений в менеджере сущностей

Подумать над большей потокобезопасностью менеджера сущностей в плане работы с коннектором субд.

вызов двух методов НачатьТранзакцию должен как-то распределяться по разным коннекторам. соответственно и сохранение/получение сущностей должно работать как-то в разных коннекторах.

При использовании ОС.Веб блокируются запросы после ошибки на уровне SQL (Коннектор SQLite)

В коннекторе SQL Lite нет обработки особождения семафора если Запрос.ВыполнитьКоманду() выкидывает исключение:

https://github.com/nixel2007/entity/blob/develop/src/%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D1%8B/%D0%9A%D0%BE%D0%BD%D0%BD%D0%B5%D0%BA%D1%82%D0%BE%D1%80SQLite.os#L175-L189

Конструктор SQL запросов

Хотелось бы видеть функционал создания sql запросов в виде конструктора в fluent стиле.
Примеры:
Дано:

  • Сущность Автор с полями: Код, Имя, Почта, Роль. Таблица бд называется Авторы.
  • Сущность Роль с полями: Код, Имя. Таблица бд называется Роли.
Конструктор = МенеджерСущностей.КонструкторЗапросов();

Выборка = Конструктор.Из("Автор") .Где("Код", 1).Выполнить();
Для Каждого ЭлементВыборки Из Выборка Цикл
    Сообщить(ЭлементВыборки.Имя);
КонецЦикла;

Возможности:

  • Из(ИмяСущности) - метод FROM
  • Где(ИмяПоля, Значение), Где(Соответствие), Где(Массив) - метод WHERE
  • Выбрать(), Выбрать("ИмяСущности.Поле1, ИмяСущности.Поле2, ИмяСущности.Поле3"), Выбрать(Массив) - указание выбора определенных полей из запроса
  • Ограничить(НомерЗаписи, КоличествоЗаписей) - метод LIMIT
  • ЛевоеСоединение(Условие), ПравоеСоединение(Условие), ВнутренееСоединение(Условие), ВнешнееСоединение(Условие) - методы LEFT JOIN, RIGHT JOIN, INNER JOIN, OUTER JOIN
  • Упорядочить(Поле, ПорядокСортировки), Упорядочик(Массив) - метод ORDER BY
  • Выполнить() - выполнить запрос

Еще пример (дано такое же):

Выборка = Конструктор
    .Из("Автор")
    .Выбрать("Автор.Имя, Автор.Почта")
    .ЛевоеСоединение("Роль", "Автор.Роль=Роль.Код")
    .Выбрать("Роль.Имя")
    .Упорядочить("Автор.Имя", ПорядокСортировки.Убывание);
    .Выполнить();

Как пример всего этого в других языках: fluentPDO

Отдельные классы, описывающие интерфейсы аннотаций

Нужен некий класс, который позволит описать все поля и допустимые типы полей аннотации, а так же "контракты" - обработчики проверки корректности значений полей аннотации.

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

Коннектор к SQLLiteWEB

Поскольку в Оскрипт.WEB своя реализация SQLLite, то и коннектор нужен свой.

Добавить поддержку типа колонку uuid

В ТипыКолонок добавить поддержку типа uuid, типа первичный ключ, автогенерируемый.
Цели:

  1. При загрузке данных из 1С чтобы элемент таблицы имел ровно тот же тип и значение ссылки что и в самой 1С.
  2. Если первичный источник данных - веб-приложение, и затем данные уходят в 1С - чтобы в 1С можно было создать ссылку с тем же идентификатором что и в самом веб-приложении.

Формирование сложных sql запросов через соединение entity

Через соединение entity формировать сложные sql запросы.
Варианты от @nixel2007:

  • Получить коннектор и вызвать у него unstable метод ВыполнитьЗапрос()
  • Можно ввести в интерфейс коннектора
  • Можно ввести в интерфейс менеджера
  • Можно в менеджер добавить ряд популярных запросов
  • Объектный флюент-построитель запросов. Например:
Авторы.Выбрать().Где("а = 0").Упорядочить()

Получение сущностей со сложным отбором

ЭлементОтбора = Новый ЭлементОтбора("ДатаРождения", ВидСравнения.Меньше, Дата(1990, 1, 1));

НайденныеФизЛица = МенеджерСущности.Получить(Тип("ФизическоеЛицо"), ЭлементОтбора);
МассивОтборов = Новый Массив;
ЭлементОтбора1 = Новый ЭлементОтбора("ДатаРождения", ВидСравнения.Меньше, Дата(1990, 1, 1));
ЭлементОтбора2 = Новый ЭлементОтбора("Имя", ВидСравнения.Равно, "Иван");

МассивОтборов.Добавить(ЭлементОтбора1);
МассивОтборов.Добавить(ЭлементОтбора2);

НайденныеФизЛица = МенеджерСущности.Получить(Тип("ФизическоеЛицо"), МассивОтборов);

Migrations: Step 1

@nixel2007: Задачу миграции я бы разделил на две больших подзадачи.

  1. создание интерфейса/дто миграции с методами Вверх() и необязательным Вниз(), принимающими в себя коннектор/какой-то новый обьект для выполнения миграций и просто выполняющие субд-специфичную строку с текстом запроса миграции (из этого механизма несколько выпадает коннектор json, но с ним отдельно надо думать). Возможно ещё и с какими-то метаданными миграции, типа версии, чтобы можно было указывать миграцию/откат до какой-то версии. Менеджер миграции, который находил бы все дто миграции из определённой папки, сортировал бы их и выполнял нужные. Таблица в бд для учёта миграций - статус, версия, время выполнения.

Коннектор к PostgreSQL

Вводная

Коннектор состоит из двух реализаций:

  • Через sql библиотеку OneScript
  • Через ИнформационнаяБаза для OneScript.Web

Создание произвольных индексов в базах данных из аннотаций сущностей

Возможность через сущность создавать произвольные индексы таблиц. Например:

Дана сущность Автор

&Идентификатор
&ГенерируемоеЗначение
&Колонка(Тип = "Целое")
Перем Код Экспорт;

Перем УчетнаяЗапись Экспорт;

Перем Имя Экспорт;

Перем ЭлектроннаяПочта Экспорт;

&Сущность(ИмяТаблицы = "Авторы")
Процедура ПриСозданииОбъекта(пКод = Неопределено)

	Если ЗначениеЗаполнено(пКод) Тогда
		Объект = МенеджерБазыДанных.АвторыМенеджер.ПолучитьОдно(пКод);	
		Если ЗначениеЗаполнено(Объект) Тогда
			ЗаполнитьЗначенияСвойств(ЭтотОбъект, Объект);
		КонецЕсли;
	КонецЕсли;

КонецПроцедуры

Мы хотим создать произвольный индекс по колонке ЭлектроннаяПочта. Добавляем аннотацию:

&Индекс(Имя = "ПроизвольныйИндекс", Колонки = "ЭлектроннаяПочта")

Идентификатор сущности вместе самой сущности при использовании ПодчиненнаяТаблица

Дано

РандомнаяСущность.os - сущность, где идентификатор:

...
&Идентификатор
&ГенерируемоеЗначение
&Колонка(Тип = "Целое")
Перем Код Экспорт;
...

Какая-то еще НоваяСущность.os с переменной:

...
&ПодчиненнаяТаблица (Тип = "Массив", ТипЭлемента = "РандомнаяСущность")
Перем Коллекция Экспорт;
...

Добавляем в нее через

НоваяСущность.Коллекция  = Новый Массив;
НоваяСущность.Добавить(РандомнаяСущность);

После записи НоваяСущность и ее повторного чтения имеем:

НоваяСущность.Коллекция[0] // Значение = 1

Расширение типов для колонок

Расширить типы для колонки:

  1. Тип &Колонка(Тип = "Тип") - чтобы в поле можно было положить например Тип("Строка")
  2. ДвоичныеДанные &Колонка(Тип = "ДвоичныеДанные ")

Получение сущностей с отбором

СтруктураПоиска = Новый Структура("Имя", "Иван");
НайденныеФизЛица = МенеджерСущностей.Получить(Тип("ФизическиеЛица"), СтруктураПоиска);

Падает сохранение объекта со вложенными таблицами, при использовании автоинкремента (SQLite)

При использовании автоинкремента в идентификаторах и ПодчиненнаяТаблица, сохранение "главной" сущности выкидывает исключение Внешнее исключение (Microsoft.Data.Sqlite.SqliteException): SQLite Error 19: 'FOREIGN KEY constraint failed'.

Пример кода:

//Файл Источник.os
&ПодчиненнаяТаблица(Тип = "Массив", ТипЭлемента = "ОписаниеПараметра")
Перем Параметры Экспорт;
МенеджерСущностей.ДобавитьКлассВМодель(Тип("ОписаниеПараметра"));
МенеджерСущностей.ДобавитьКлассВМодель(Тип("ОписаниеИсточника"));
МенеджерСущностей.Инициализировать();

Параметр = Параметры.Создать();
Параметр.Имя = "Тест";
Параметр.Значение = "фывыфв";
Параметр.Сохранить();

Источник = Источники.Создать();
Источник.Наименование = "Тест";
Источник.Тип = "Файл";
Источник.Параметры.Добавить(Параметр);
Источник.Сохранить();

Ошибка при редактировании Сущности - генерируется новый идентификатор

src\oscript_modules\entity\src\Классы\КоннекторPostgreSQL.os

Процедура Сохранить()
...

Запрос.ВыполнитьКоманду();

Если ОбъектМодели.Идентификатор().ГенерируемоеЗначение Тогда

	Если ЭтоВставкаОбъекта Тогда

...

ЗАМЕНИТЬ НА:

Запрос.ВыполнитьКоманду();

Если ОбъектМодели.Идентификатор().ГенерируемоеЗначение И НЕ ЗначениеЗаполнено(ОбъектМодели.ПолучитьЗначениеИдентификатора(Сущность)) Тогда

	Если ЭтоВставкаОбъекта Тогда

...

  • в этой же процедуре убрать:
    Сообщить(ТекстЗапроса);

Если две сущности ссылаются друг на друга, то foreing key надо создавать отложенно, через alter column

Что-то среднее между багом и улучшением.

Две таблицы с FK друг на друга не могут быть последовательно созданы - от СУБД придет эксепшен при накладывании констрэйта FK.
Инициализация должна идти по принципу создания таблиц с типами, без контстрейта FK, а потом через ALTER COLUMN уже проставлять FK.

Migrations: Step 2

@nixel2007: Задачу миграции я бы разделил на две больших подзадачи.

  1. разработка способа работы с DML в виде объектной модели. Для этого можно завести новый интерфейс для коннектора с методами ДобавитьКолонку, ПереименоватьКолонку, ДобавитьИндекс и такое все. Чтобы можно было во fluent режиме в виде билдера описать изменение схемы, а на выходе получить текст запроса для alter table, который опять таки можно пихнуть в коннектор и выполнить.

С точки зрения разработчика нужно будет класть дто с миграциями в спец папочку и указать в конфиге/коде приложения текущую версию. Всё остальное брал бы на себя энтити.

Над версионированием самой схемы и построения версия-специфичных объектов модели я думал, но кажется это можно отложить на третью очередь разработки, ближе к механизму генерации миграций, но возможно оно понадобится уже на втором шаге, надо побольше покурить.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.