diff --git a/.gitignore b/.gitignore index e57a416..ffcf25b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.db *.ospx + +coverage/ +tests.xml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..469f0d1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "language-1c-bsl.linterEntryPoint": "./src/fake-entrypoint.os" +} \ No newline at end of file diff --git a/README.md b/README.md index ddbc755..5de4a9b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # entity - OneScript Persistence API - [![GitHub release](https://img.shields.io/github/release/nixel2007/entity.svg?style=flat-square)](https://github.com/nixel2007/entity/releases) [![GitHub license](https://img.shields.io/github/license/nixel2007/entity.svg?style=flat-square)](https://github.com/nixel2007/entity/blob/develop/LICENSE.md) @@ -139,7 +138,7 @@ // В метод можно передать отбор в виде соответствия, аналогично методу Получить() СохраненноеФизЛицо = МенеджерСущностей.ПолучитьОдно(Тип("ФизическоеЛицо"), Отбор); -// Если вызвать метод с параметром не-соответствием, то будет осуществлен поиск по идентификатору сущности. +// Если вызвать метод "ПолучитьОдно" с параметром не-соответствием, то будет осуществлен поиск по идентификатору сущности. Идентификатор = 123; СохраненноеФизЛицо = МенеджерСущностей.ПолучитьОдно(Тип("ФизическоеЛицо"), Идентификатор); ``` @@ -162,13 +161,45 @@ ## Удаление сущностей ```bsl - // Допустим имеется сущность, которую надо удалить. МенеджерСущностей.Удалить(СущностьФизическоеЛицо); // После выполнения метода в БД не останется строки с идентификатором, равным идентификатору сущности +``` + +## Работа через ХранилищеСущностей + +Для упрощения взаимодействия с библиотекой помимо МенеджераСущностей, подразумевающего постоянную передачу *типа сущности*, осуществлять операции над сущностями можно через ХранилищеСущностей. +Хранилище сущностей предоставляет тот же базовый интерфейс, что и МенеджерСущности, но не требует передачи типа сущности как параметра. + +```bsl +// Получение хранилища сущностей +ХранилищеФизЛиц = МенеджерСущностей.ПолучитьХранилищеСущностей(Тип("ФизическоеЛицо")); + +// Поиск сущностей +Идентификатор = 1; +ФизЛицо = ХранилищеФизЛиц.ПолучитьОдно(Идентификатор); + +ФизЛицо.Имя = "Петр"; + +ХранилищеФизЛиц.Сохранить(ФизЛицо); +``` + +### Работа с транзакциями + +Методы по работе с транзакциями есть как в Менеджере сущностей, так и в Хранилище сущностей. + +Транзакционность поддерживается в рамках экземпляра менеджера сущностей или хранилища сущностей. При необходимости работы с транзакциями с несколькими типами сущностей следует использовать методы работы с транзакциями в Менеджере сущностей и модифицировать сущности через него же. + +```bsl +МенеджерСущностей.НачатьТранзацию(); + +// Объекты ФизическоеЛицо и СтранаМира из примеров выше: +МенеджерСущностей.Сохранить(СтранаМира); +МенеджерСущностей.Сохранить(СохраняемоеФизЛицо); +МенеджерСущностей.ЗафиксироватьТранзакцию(); ``` ## Система аннотаций для сущностей @@ -239,7 +270,15 @@ * Трансляция запросов от прикладной логики к коннекторам * Конструирование найденных сущностей по данным, возвращаемым коннекторами -### Коннекторы +### ХранилищеСущностей + +ХранилищеСущностей предоставляет тот же интерфейс по работе с сущностями и транзакциями, но с глобальной привязкой к конкретному типу сущности. Для получения ХранилищаСущностей служит метод `МенеджерСущностей::ПолучитьХранилищеСущностей`. + +В отличие от МенеджераСущностей, ХранилищеСущностей не требует передачи в методы параметра "ТипСущности". + +Хранилища сущностей и пулы сущностей совпадают в рамках одного типа сущности, типа коннектора и строки соединения. Другими словами, два менеджера сущности, проинициализированные одним и тем же коннектором и строкой соединения, вернут одинаковые хранилища сущностей одного типа. + +### Коннекторы (АбстрактныйКоннектор) Коннектор содержит в себе логику по работе с конкретной СУБД. Например, `КоннекторSQLite` служит для оперирования СУБД SQLite. В зоне ответственности коннектора находятся: @@ -256,6 +295,10 @@ Например, `КоннекторJSON` не умеет работать с транзакциями, однако, он имеет соответствующие методы, выводящие диагностические сообщения при их вызове. +> Важно! + +Каждое ХранилищеСущностей и МенеджерСущностей хранят в себе отдельные экземпляры Коннекторов. Тип, строка соединения и параметры коннектора определяются при создании МенеджераСущностей. + ### МодельДанных Модель данных хранит в себе список всех зарегистрированных классов-сущности в виде ОбъектовМодели @@ -277,6 +320,16 @@ Помимо мета-информации ОбъектМодели позволяет получать значения колонок таблицы на основании имен полей сущности (и наоборот), вычислять значение идентификатора сущности, выполнять приведение типов и установку значений полей сущности. +### КоннекторSQLite + +В состав библиотеки входит референсная реализация интерфейса коннектора в виде коннектора к СУБД SQLite. Реализация базируется на библиотеке [sql](https://github.com/oscript-library/sql). + +Коннектор поддерживает все CRUD-операции над сущностями, простой и сложный поиск, работу с транзакциями. + +> Внимание! + +При использовании in-memory базы данных в моделях больше, чем с одним типом сущности, строка соединения должна выглядеть так: `"FullUri=file::memory:?cache=shared"` + ## Версионирование и обратная совместимость Библиотека `entity` в целом следует концепции [семантического версионирования](https://semver.org/) со следующими изменениями в правилах нумерации версий: @@ -294,13 +347,24 @@ Под контроль и обязательство соблюдения обратной совместимости попадают: * для Major.Entity: - * все публичные методы классов "МенеджерСущности", "МодельДанных", "ОбъектМодели", "ЭлементОтбора", непомеченные как нестабильные (`@unstable`) в описании метода; - * значения модулей-перечислений "ТипыКолонок" и "ВидСравнения"; + * все публичные непомеченные как "нестабильные" (`@unstable`) или "для служебного использования" (`@internal`) методы классов: + * [`МенеджерСущности`](src/Классы/МенеджерСущностей.os), + * [`ХранилищеСущностей`](src/Классы/ХранилищеСущностей.os), + * [`МодельДанных`](src/Классы/МодельДанных.os), + * [`ОбъектМодели`](src/Классы/ОбъектМодели.os), + * [`ЭлементОтбора`](src/Классы/ЭлементОтбора.os); + * значения модулей-перечислений: + * [`ТипыКолонок`](src/Модули/ТипыКолонок.os), + * [`ВидСравнения`](src/Модули/ВидСравнения.os); * состав и параметры аннотаций сущностей; * для Major.Connector: - * все публичные методы класса "АбстрактныйКоннектор" и их сигнатуры; - * все публичные методы классов "МодельДанных", "ОбъектМодели", "ЭлементОтбора", непомеченные как нестабильные - (`@unstable`) в описании метода. - * значения модулей-перечислений "ТипыКолонок" и "ВидСравнения"; + * все публичные методы класса [`АбстрактныйКоннектор`](src/Классы/АбстрактныйКоннектор.os) и их сигнатуры; + * все публичные непомеченные как "нестабильные" (`@unstable`) методы классов: + * [`МодельДанных`](src/Классы/МодельДанных.os), + * [`ОбъектМодели`](src/Классы/ОбъектМодели.os), + * [`ЭлементОтбора`](src/Классы/ЭлементОтбора.os); + * значения модулей-перечислений: + * [`ТипыКолонок`](src/Модули/ТипыКолонок.os), + * [`ВидСравнения`](src/Модули/ВидСравнения.os). > To be continued... diff --git a/lib.config b/lib.config index 42355aa..e2ae8f6 100644 --- a/lib.config +++ b/lib.config @@ -5,6 +5,7 @@ + \ No newline at end of file diff --git a/packagedef b/packagedef index 59c69eb..f1adafb 100644 --- a/packagedef +++ b/packagedef @@ -3,7 +3,6 @@ // Полную документацию см. на hub.oscript.io/packaging // - Описание.Имя("entity") .Версия("2.2.0.0") .Автор("Nikita Gryzlov") @@ -18,9 +17,7 @@ .ЗависитОт("asserts", "1.1.1") .ЗависитОт("logos", "1.2.0") .ЗависитОт("reflector", "0.6.0") + .ЗависитОт("semaphore", "1.0.1") .ЗависитОт("sql", "1.1.0.0") .ЗависитОт("strings", "0.4.1") - //.ЗависитОт("package2", ">=1.1", "<2.0") - //.ОпределяетКласс("УправлениеВселенной", "src/universe-mngr.os") - //.ОпределяетМодуль("ПолезныеФункции", "src/tools.os") ; diff --git a/src/fake-entrypoint.os b/src/fake-entrypoint.os new file mode 100644 index 0000000..614c512 --- /dev/null +++ b/src/fake-entrypoint.os @@ -0,0 +1 @@ +#Использовать ".." diff --git "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\240\320\260\320\261\320\276\321\202\320\260\320\241\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200\320\260\320\274\320\270.os" "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\240\320\260\320\261\320\276\321\202\320\260\320\241\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200\320\260\320\274\320\270.os" new file mode 100644 index 0000000..060dfe0 --- /dev/null +++ "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\240\320\260\320\261\320\276\321\202\320\260\320\241\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200\320\260\320\274\320\270.os" @@ -0,0 +1,229 @@ +#Использовать asserts + +Перем СвойстваКоннекторов; + +Функция СоздатьКоннектор(ТипКоннектора) Экспорт + Коннектор = Новый(ТипКоннектора); + СвойстваКоннектора = Конструктор_СвойстваКоннектора(); + СвойстваКоннекторов.Вставить(Коннектор, СвойстваКоннектора); + + Возврат Коннектор; +КонецФункции + +Процедура ОткрытьКоннектор(Коннектор, СтрокаСоединения, ПараметрыКоннектора) Экспорт + СвойстваКоннектора = СвойстваКоннекторов.Получить(Коннектор); + СвойстваКоннектора.СтрокаСоединения = СтрокаСоединения; + СвойстваКоннектора.Параметры = ПараметрыКоннектора; + + Коннектор.Открыть(СтрокаСоединения, ПараметрыКоннектора); +КонецПроцедуры + +Процедура ЗакрытьКоннектор(Коннектор) Экспорт + Если Коннектор.Открыт() Тогда + Коннектор.Закрыть(); + КонецЕсли; +КонецПроцедуры + +Функция Сохранить(Коннектор, ОбъектМодели, ПулСущностей, Сущность) Экспорт + ТипСущности = ТипЗнч(Сущность); + + ПроверитьЧтоКлассЯвляетсяСущностью(ТипСущности); + ПроверитьЧтоТипСущностиЗарегистрированВМодели(ОбъектМодели); + ПроверитьНеобходимостьЗаполненияИдентификатора(ОбъектМодели, Сущность); + + Коннектор.Сохранить(ОбъектМодели, Сущность); + + ПулСущностей.Вставить(ОбъектМодели.ПолучитьЗначениеИдентификатора(Сущность), Сущность); +КонецФункции + +Функция Получить(Коннектор, ОбъектМодели, ПулСущностей, Отбор = Неопределено) Экспорт + Колонки = ОбъектМодели.Колонки(); + + ПередаваемыйОтбор = Новый Массив; + + Если ТипЗнч(Отбор) = Тип("Соответствие") Тогда + // Переформируем ключи отбора из имен полей в имена колонок + Для Каждого КлючИЗначение Из Отбор Цикл + Колонка = Колонки.Найти(КлючИЗначение.Ключ, "ИмяПоля"); + Ожидаем.Что( + Колонка, + СтрШаблон("Не удалось найти данные о колонке по имени поля %1", КлючИЗначение.Ключ) + ).Не_().Равно(Неопределено); + + ПередаваемыйОтбор.Добавить(Новый ЭлементОтбора(Колонка.ИмяКолонки, ВидСравнения.Равно, КлючИЗначение.Значение)); + КонецЦикла; + ИначеЕсли ТипЗнч(Отбор) = Тип("Массив") Тогда + Для Каждого ЭлементОтбора Из Отбор Цикл + Колонка = Колонки.Найти(ЭлементОтбора.ПутьКДанным, "ИмяПоля"); + Ожидаем.Что( + Колонка, + СтрШаблон("Не удалось найти данные о колонке по имени поля %1", ЭлементОтбора.ПутьКДанным) + ).Не_().Равно(Неопределено); + + ПередаваемыйОтбор.Добавить(Новый ЭлементОтбора(Колонка.ИмяКолонки, ЭлементОтбора.ВидСравнения, ЭлементОтбора.Значение)); + КонецЦикла; + ИначеЕсли ТипЗнч(Отбор) = Тип("ЭлементОтбора") Тогда + ЭлементОтбора = Отбор; + Колонка = Колонки.Найти(ЭлементОтбора.ПутьКДанным, "ИмяПоля"); + Ожидаем.Что( + Колонка, + СтрШаблон("Не удалось найти данные о колонке по имени поля %1", ЭлементОтбора.ПутьКДанным) + ).Не_().Равно(Неопределено); + + ПередаваемыйОтбор.Добавить(Новый ЭлементОтбора(Колонка.ИмяКолонки, ЭлементОтбора.ВидСравнения, ЭлементОтбора.Значение)); + ИначеЕсли Отбор = Неопределено Тогда + // no-op + Иначе + ВызватьИсключение "В метод получения данных передан неожиданный тип отбора: " + ТипЗнч(Отбор); + КонецЕсли; + + НайденныеСущности = Новый Массив; + + НайденныеСтроки = Коннектор.НайтиСтрокиВТаблице(ОбъектМодели, ПередаваемыйОтбор); + Если НайденныеСтроки.Количество() = 0 Тогда + Возврат НайденныеСущности; + КонецЕсли; + + Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл + ЗначениеИдентификатора = НайденнаяСтрока.Получить(ОбъектМодели.Идентификатор().ИмяКолонки); + ЗначениеИдентификатора = ОбъектМодели.ПривестиЗначениеПоля( + ЗначениеИдентификатора, + ОбъектМодели.Идентификатор().ИмяПоля + ); + Сущность = ПулСущностей.Получить(ЗначениеИдентификатора); + Если Сущность = Неопределено Тогда + Сущность = Новый(ОбъектМодели.ТипСущности()); + ПулСущностей.Вставить(ЗначениеИдентификатора, Сущность); + КонецЕсли; + + Для Каждого Колонка Из Колонки Цикл + ЗначениеКолонки = НайденнаяСтрока.Получить(Колонка.ИмяКолонки); + Если Колонка.ТипКолонки = ТипыКолонок.Ссылка И ЗначениеЗаполнено(ЗначениеКолонки) Тогда + + Если Колонка.ТипСсылки = ОбъектМодели.ТипСущности() И ЗначениеКолонки = ЗначениеИдентификатора Тогда + ЗначениеКолонки = Сущность; + Иначе + ХранилищеСущностейСсылки = ХранилищаСущностей.Получить( + ОбъектМодели.МодельДанных().Получить(Колонка.ТипСсылки), + Коннектор + ); + ЗначениеКолонки = ХранилищеСущностейСсылки.ПолучитьОдно(ЗначениеКолонки); + КонецЕсли; + КонецЕсли; + ОбъектМодели.УстановитьЗначениеКолонкиВПоле(Сущность, Колонка.ИмяКолонки, ЗначениеКолонки); + КонецЦикла; + + НайденныеСущности.Добавить(Сущность); + КонецЦикла; + + Возврат НайденныеСущности; +КонецФункции + +Функция ПолучитьОдно(Коннектор, ОбъектМодели, ПулСущностей, Знач Отбор = Неопределено) Экспорт + + Если Отбор = Неопределено Тогда + ПередаваемыйОтбор = Отбор; + ИначеЕсли ТипЗнч(Отбор) = Тип("Соответствие") Тогда + ПередаваемыйОтбор = Отбор; + ИначеЕсли ТипЗнч(Отбор) = Тип("Массив") Тогда + ПередаваемыйОтбор = Отбор; + ИначеЕсли ТипЗнч(Отбор) = Тип("ЭлементОтбора") Тогда + ПередаваемыйОтбор = Отбор; + Иначе + ПередаваемыйОтбор = Новый Соответствие(); + ПередаваемыйОтбор.Вставить(ОбъектМодели.Идентификатор().ИмяПоля, Отбор); + КонецЕсли; + + НайденныеСущности = Получить(Коннектор, ОбъектМодели, ПулСущностей, ПередаваемыйОтбор); + + Если НайденныеСущности.Количество() = 0 Тогда + Возврат Неопределено; + Иначе + Возврат НайденныеСущности[0]; + КонецЕсли; + +КонецФункции + +// Удаляет удаление сущности из базы данных. +// Сущность должна иметь заполненный идентификатор. +// +// Параметры: +// Сущность - Произвольный - Удаляемая сущность +// +Процедура Удалить(Коннектор, ОбъектМодели, ПулСущностей, Сущность) Экспорт + Коннектор.Удалить(ОбъектМодели, Сущность); + ПулСущностей.Удалить(Сущность); +КонецПроцедуры + +// Посылает коннектору запрос на начало транзакции. +// +Процедура НачатьТранзакцию(Коннектор) Экспорт + Коннектор.НачатьТранзакцию(); +КонецПроцедуры + +// Посылает коннектору запрос на фиксацию транзакции. +// +Процедура ЗафиксироватьТранзакцию(Коннектор) Экспорт + Коннектор.ЗафиксироватьТранзакцию(); +КонецПроцедуры + +// Посылает коннектору запрос на отмену транзакции. +// +Процедура ОтменитьТранзакцию(Коннектор) Экспорт + Коннектор.ОтменитьТранзакцию(); +КонецПроцедуры + +// Возвращает дополнительные свойства коннектора +// +// Параметры: +// Коннектор - АбстрактныйКоннектор - Коннектор, свойства которого необходимо получить. +// +// Возвращаемое значение: +// Структура - Дополнительные свойства коннектора. см. Конструктор_СвойстваКоннектора() +// +Функция ПолучитьСвойстваКоннектора(Коннектор) Экспорт + Возврат СвойстваКоннекторов.Получить(Коннектор); +КонецФункции + +// <Описание процедуры> +// +// Параметры: +// ТипКласса - Тип - Тип, в котором проверяется наличие необходимых аннотаций. +// +Процедура ПроверитьЧтоКлассЯвляетсяСущностью(ТипКласса) + + РефлекторОбъекта = Новый РефлекторОбъекта(ТипКласса); + ТаблицаМетодов = РефлекторОбъекта.ПолучитьТаблицуМетодов("Сущность", Ложь); + Ожидаем.Что(ТаблицаМетодов, СтрШаблон("Класс %1 не имеет аннотации &Сущность", ТипКласса)).ИмеетДлину(1); + + ТаблицаСвойств = РефлекторОбъекта.ПолучитьТаблицуСвойств("Идентификатор"); + Ожидаем.Что(ТаблицаСвойств, СтрШаблон("Класс %1 не имеет поля с аннотацией &Идентификатор", ТипКласса)).ИмеетДлину(1); + +КонецПроцедуры + +Процедура ПроверитьЧтоТипСущностиЗарегистрированВМодели(ОбъектМодели) + // TODO: проверка должна быть в момент получения репозитория + Ожидаем.Что(ОбъектМодели, "Тип сущности не зарегистрирован в модели данных").Не_().Равно(Неопределено); +КонецПроцедуры + +Процедура ПроверитьНеобходимостьЗаполненияИдентификатора(ОбъектМодели, Сущность) + Если ОбъектМодели.Идентификатор().ГенерируемоеЗначение Тогда + Возврат; + КонецЕсли; + + ЗначениеИдентификатора = ОбъектМодели.ПолучитьЗначениеИдентификатора(Сущность); + Ожидаем.Что( + ЗначениеИдентификатора, СтрШаблон("Сущность с типом %1 должна иметь заполненный идентификатор", Тип(Сущность)) + ).Заполнено(); + +КонецПроцедуры + +Функция Конструктор_СвойстваКоннектора() + СвойстваКоннектора = Новый Структура; + СвойстваКоннектора.Вставить("СтрокаСоединения"); + СвойстваКоннектора.Вставить("Параметры"); + + Возврат СвойстваКоннектора; +КонецФункции + +СвойстваКоннекторов = Новый Соответствие(); diff --git "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\260\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\260\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" new file mode 100644 index 0000000..b056554 --- /dev/null +++ "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\260\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" @@ -0,0 +1,64 @@ +#Использовать semaphore + +Перем Хранилища; +Перем Семафор; + +Функция Получить(ОбъектМодели, Коннектор) Экспорт + + Семафор.Захватить(); + + СвойстваКоннектора = РаботаСКоннекторами.ПолучитьСвойстваКоннектора(Коннектор); + + КлючХранилища = ПолучитьКлючХранилища(ОбъектМодели.ТипСущности(), ТипЗнч(Коннектор), СвойстваКоннектора.СтрокаСоединения); + ХранилищеСущностей = Хранилища.Получить(КлючХранилища); + Если ХранилищеСущностей = Неопределено Тогда + ХранилищеСущностей = Новый ХранилищеСущностей( + ОбъектМодели, + ТипЗнч(Коннектор), + СвойстваКоннектора.СтрокаСоединения, + СвойстваКоннектора.Параметры + ); + Хранилища.Вставить(КлючХранилища, ХранилищеСущностей); + КонецЕсли; + + Семафор.Освободить(); + + Возврат ХранилищеСущностей; + +КонецФункции + +Процедура Закрыть(ТипКоннектора, СтрокаСоединения, ПараметрыКоннектора) Экспорт + + Семафор.Захватить(1000); + + ЗакрываемыеХранилища = Новый Массив; + Для Каждого КлючИЗначение Из Хранилища Цикл + Хранилище = КлючИЗначение.Значение; + КоннекторХранилища = Хранилище.ПолучитьКоннектор(); + СвойстваКоннектора = РаботаСКоннекторами.ПолучитьСвойстваКоннектора(КоннекторХранилища); + + Если ТипКоннектора = ТипЗнч(КоннекторХранилища) И СтрокаСоединения = СвойстваКоннектора.СтрокаСоединения Тогда + Хранилище.Закрыть(); + ЗакрываемыеХранилища.Добавить(КлючИЗначение.Ключ); + КонецЕсли; + КонецЦикла; + + Для Каждого ЗакрываемоеХранилище Из ЗакрываемыеХранилища Цикл + Хранилища.Удалить(ЗакрываемоеХранилище); + КонецЦикла; + + Семафор.Освободить(); + +КонецПроцедуры + +Функция ПолучитьКлючХранилища(ТипСущности, ТипКоннектора, СтрокаСоединения) + Возврат СтрШаблон( + "%1 - %2 - %3", + ТипСущности, + ТипКоннектора, + СтрокаСоединения + ); +КонецФункции + +Хранилища = Новый Соответствие(); +Семафор = Новый Семафор(); diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200SQLite.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200SQLite.os" index edcf1b0..ef608c0 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200SQLite.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\320\275\320\265\320\272\321\202\320\276\321\200SQLite.os" @@ -166,7 +166,6 @@ ИменаКолонок = ИменаКолонок + Символы.Таб + ДанныеОКолонке.ИмяКолонки + "," + Символы.ПС; ЗначенияКолонок = ЗначенияКолонок + Символы.Таб + "@" + ДанныеОКолонке.ИмяКолонки + "," + Символы.ПС; - ЗначениеПараметра = ОбъектМодели.ПолучитьПриведенноеЗначениеПоля(Сущность, ДанныеОКолонке.ИмяПоля); Запрос.УстановитьПараметр(ДанныеОКолонке.ИмяКолонки, ЗначениеПараметра); КонецЦикла; diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" index 0864480..04a714c 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" @@ -1,3 +1,5 @@ +#Использовать "../internal" + #Использовать asserts #Использовать logos #Использовать reflector @@ -25,14 +27,14 @@ ПроверитьПоддержкуИнтерфейсаКоннектора(ТипКоннектора); МодельДанных = Новый МодельДанных; - - Коннектор = Новый(ТипКоннектора); - + + Коннектор = РаботаСКоннекторами.СоздатьКоннектор(ТипКоннектора); + СтрокаСоединенияКоннектора = СтрокаСоединения; Если ППараметрыКоннектора = Неопределено Тогда ПараметрыКоннектора = Новый Массив; Иначе - ПараметрыКоннектора = ППараметрыКоннектора; + ПараметрыКоннектора = ППараметрыКоннектора; КонецЕсли; КонецПроцедуры @@ -43,14 +45,14 @@ // Процедура ДобавитьКлассВМодель(ТипСущности) Экспорт ПроверитьЧтоКлассЯвляетсяСущностью(ТипСущности); - + МодельДанных.СоздатьОбъектМодели(ТипСущности); КонецПроцедуры // Запускает процессы инициализации коннектора и таблиц БД. // Процедура Инициализировать() Экспорт - Коннектор.Открыть(СтрокаСоединенияКоннектора, ПараметрыКоннектора); + РаботаСКоннекторами.ОткрытьКоннектор(Коннектор, СтрокаСоединенияКоннектора, ПараметрыКоннектора); ОбъектыМодели = МодельДанных.ПолучитьОбъектыМодели(); Для Каждого ОбъектМодели Из ОбъектыМодели Цикл Коннектор.ИнициализироватьТаблицу(ОбъектМодели); @@ -64,27 +66,22 @@ // Процедура Сохранить(Сущность) Экспорт ТипСущности = ТипЗнч(Сущность); - - ПроверитьЧтоКлассЯвляетсяСущностью(ТипСущности); - ПроверитьЧтоТипСущностиЗарегистрированВМодели(ТипСущности); - ПроверитьНеобходимостьЗаполненияИдентификатора(Сущность); - ОбъектМодели = МодельДанных.Получить(ТипСущности); - - Коннектор.Сохранить(ОбъектМодели, Сущность); + ПулСущностей = ПолучитьПулСущностей(ТипСущности); + РаботаСКоннекторами.Сохранить(Коннектор, ОбъектМодели, ПулСущностей, Сущность); КонецПроцедуры // Осуществляет поиск сущностей переданного типа по идентификатору. // // Параметры: // ТипСущности - Тип - Тип искомой сущности. -// Отбор - Произвольный - Отбор для поиска. +// Отбор - Произвольный - Отбор для поиска. // Если параметр не задан или равен "Неопределено", то возвращаются все найденные сущности указанного типа. // Если параметр имеет тип "Соответствие", то каждое значение соответствия преобразуется к условию поиска // ИмяПоля = ЗначениеПоля, где ИмяПоля - ключ элемента соответствия, ЗначениеПоля - значение элемента соответствия. // Если параметр имеет тип "Массив", то каждое элемент массива должен иметь тип "ЭлементОтбора". // Каждый элемент отбора преобразуется к условию поиска. В качестве "ПутьКДанным" указываются имена полей. -// Если параметр имеет тип "ЭлементОтбора", то элемент отбора преобразуется к условию поиска. +// Если параметр имеет тип "ЭлементОтбора", то элемент отбора преобразуется к условию поиска. // В качестве "ПутьКДанным" указываются имена полей. // // Возвращаемое значение: @@ -92,179 +89,135 @@ // экземпляры класса с типом, равным переданному "ТипуСущности", с заполненными значениями полей. // Функция Получить(ТипСущности, Отбор = Неопределено) Экспорт - ОбъектМодели = МодельДанных.Получить(ТипСущности); - Колонки = ОбъектМодели.Колонки(); - - ПередаваемыйОтбор = Новый Массив; - - Если ТипЗнч(Отбор) = Тип("Соответствие") Тогда - // Переформируем ключи отбора из имен полей в имена колонок - Для Каждого КлючИЗначение Из Отбор Цикл - Колонка = Колонки.Найти(КлючИЗначение.Ключ, "ИмяПоля"); - Ожидаем.Что( - Колонка, - СтрШаблон("Не удалось найти данные о колонке по имени поля %1", КлючИЗначение.Ключ) - ).Не_().Равно(Неопределено); - - ПередаваемыйОтбор.Добавить(Новый ЭлементОтбора(Колонка.ИмяКолонки, ВидСравнения.Равно, КлючИЗначение.Значение)); - КонецЦикла; - ИначеЕсли ТипЗнч(Отбор) = Тип("Массив") Тогда - Для Каждого ЭлементОтбора Из Отбор Цикл - Колонка = Колонки.Найти(ЭлементОтбора.ПутьКДанным, "ИмяПоля"); - Ожидаем.Что( - Колонка, - СтрШаблон("Не удалось найти данные о колонке по имени поля %1", ЭлементОтбора.ПутьКДанным) - ).Не_().Равно(Неопределено); - - ПередаваемыйОтбор.Добавить(Новый ЭлементОтбора(Колонка.ИмяКолонки, ЭлементОтбора.ВидСравнения, ЭлементОтбора.Значение)); - КонецЦикла; - ИначеЕсли ТипЗнч(Отбор) = Тип("ЭлементОтбора") Тогда - ЭлементОтбора = Отбор; - Колонка = Колонки.Найти(ЭлементОтбора.ПутьКДанным, "ИмяПоля"); - Ожидаем.Что( - Колонка, - СтрШаблон("Не удалось найти данные о колонке по имени поля %1", ЭлементОтбора.ПутьКДанным) - ).Не_().Равно(Неопределено); - - ПередаваемыйОтбор.Добавить(Новый ЭлементОтбора(Колонка.ИмяКолонки, ЭлементОтбора.ВидСравнения, ЭлементОтбора.Значение)); - ИначеЕсли Отбор = Неопределено Тогда - // no-op - Иначе - ВызватьИсключение "В метод получения данных передан неожиданный тип отбора: " + ТипЗнч(Отбор); - КонецЕсли; - - НайденныеСущности = Новый Массив; - - НайденныеСтроки = Коннектор.НайтиСтрокиВТаблице(ОбъектМодели, ПередаваемыйОтбор); - Если НайденныеСтроки.Количество() = 0 Тогда - Возврат НайденныеСущности; - КонецЕсли; - - Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл - Сущность = Новый(ТипСущности); - - Для Каждого Колонка Из Колонки Цикл - ЗначениеКолонки = НайденнаяСтрока.Получить(Колонка.ИмяКолонки); - Если Колонка.ТипКолонки = ТипыКолонок.Ссылка И ЗначениеЗаполнено(ЗначениеКолонки) Тогда - ЗначениеКолонки = ПолучитьОдно(Колонка.ТипСсылки, ЗначениеКолонки); - КонецЕсли; - ОбъектМодели.УстановитьЗначениеКолонкиВПоле(Сущность, Колонка.ИмяКолонки, ЗначениеКолонки); - КонецЦикла; - - НайденныеСущности.Добавить(Сущность); - КонецЦикла; - - Возврат НайденныеСущности; - + ПулСущностей = ПолучитьПулСущностей(ТипСущности); + Возврат РаботаСКоннекторами.Получить(Коннектор, ОбъектМодели, ПулСущностей, Отбор); КонецФункции // Осуществляет поиск сущности переданного типа по идентификатору. // // Параметры: // ТипСущности - Тип - Тип искомой сущности. -// Отбор - Произвольный - Отбор для поиска. +// Отбор - Произвольный - Отбор для поиска. // Если параметр не задан или равен "Неопределено", то возвращаются все найденные сущности указанного типа. // Если параметр имеет тип "Соответствие", то каждое значение соответствия преобразуется к условию поиска. // ИмяПоля = ЗначениеПоля, где ИмяПоля - ключ элемента соответствия, ЗначениеПоля - значение элемента соответствия. // Если параметр имеет тип "Массив", то каждое элемент массива должен иметь тип "ЭлементОтбора". // Каждый элемент отбора преобразуется к условию поиска. В качестве "ПутьКДанным" указываются имена полей. -// Если параметр имеет тип "ЭлементОтбора", то элемент отбора преобразуется к условию поиска. +// Если параметр имеет тип "ЭлементОтбора", то элемент отбора преобразуется к условию поиска. // В качестве "ПутьКДанным" указываются имена полей. // Любой другой тип интерпретируется как поиск по &Идентификатору. // // Возвращаемое значение: -// Произвольный - Если сущность была найдена, то возвращается экземпляр класса с типом, равным переданному +// Произвольный - Если сущность была найдена, то возвращается экземпляр класса с типом, равным переданному // "ТипуСущности", с заполненными значениями полей. Иначе возвращается "Неопределено". // Функция ПолучитьОдно(ТипСущности, Знач Отбор = Неопределено) Экспорт ОбъектМодели = МодельДанных.Получить(ТипСущности); - - Если Отбор = Неопределено Тогда - ПередаваемыйОтбор = Отбор; - ИначеЕсли ТипЗнч(Отбор) = Тип("Соответствие") Тогда - ПередаваемыйОтбор = Отбор; - ИначеЕсли ТипЗнч(Отбор) = Тип("Массив") Тогда - ПередаваемыйОтбор = Отбор; - ИначеЕсли ТипЗнч(Отбор) = Тип("ЭлементОтбора") Тогда - ПередаваемыйОтбор = Отбор; - Иначе - ПередаваемыйОтбор = Новый Соответствие(); - ПередаваемыйОтбор.Вставить(ОбъектМодели.Идентификатор().ИмяПоля, Отбор); - КонецЕсли; - - НайденныеСущности = Получить(ТипСущности, ПередаваемыйОтбор); - - Если НайденныеСущности.Количество() = 0 Тогда - Возврат Неопределено; - Иначе - Возврат НайденныеСущности[0]; - КонецЕсли; + ПулСущностей = ПолучитьПулСущностей(ТипСущности); + Возврат РаботаСКоннекторами.ПолучитьОдно(Коннектор, ОбъектМодели, ПулСущностей, Отбор); КонецФункции -// Удаляет удаление сущности из базы данных. +// Выполняет удаление сущности из базы данных. // Сущность должна иметь заполненный идентификатор. // // Параметры: // Сущность - Произвольный - Удаляемая сущность // -Функция Удалить(Сущность) Экспорт - ОбъектМодели = МодельДанных.Получить(ТипЗнч(Сущность)); - Коннектор.Удалить(ОбъектМодели, Сущность); -КонецФункции +Процедура Удалить(Сущность) Экспорт + ТипСущности = ТипЗнч(Сущность); + ОбъектМодели = МодельДанных.Получить(ТипСущности); + ПулСущностей = ПолучитьПулСущностей(ТипСущности); + РаботаСКоннекторами.Удалить(Коннектор, ОбъектМодели, ПулСущностей, Сущность); +КонецПроцедуры -// Посылает коннектору запрос на закрытие соединения. +// Выполняет очистку полную данных библиотеки. +// Дополнительно посылает всем используемым коннекторам запросы на закрытие соединения. // Процедура Закрыть() Экспорт - Если Коннектор.Открыт() Тогда - Коннектор.Закрыть(); - КонецЕсли; + РаботаСКоннекторами.ЗакрытьКоннектор(Коннектор); МодельДанных.Очистить(); + СвойстваКоннектора = РаботаСКоннекторами.ПолучитьСвойстваКоннектора(Коннектор); + ХранилищаСущностей.Закрыть(ТипЗнч(Коннектор), СвойстваКоннектора.СтрокаСоединения, СвойстваКоннектора.Параметры); + // Для освобожения ссылок на все коннекторы и соединения с СУБД + ВыполнитьСборкуМусора(); КонецПроцедуры // Посылает коннектору запрос на начало транзакции. // Процедура НачатьТранзакцию() Экспорт - Коннектор.НачатьТранзакцию(); + РаботаСКоннекторами.НачатьТранзакцию(Коннектор); КонецПроцедуры // Посылает коннектору запрос на фиксацию транзакции. // Процедура ЗафиксироватьТранзакцию() Экспорт - Коннектор.ЗафиксироватьТранзакцию(); + РаботаСКоннекторами.ЗафиксироватьТранзакцию(Коннектор); КонецПроцедуры // Посылает коннектору запрос на отмену транзакции. // Процедура ОтменитьТранзакцию() Экспорт - Коннектор.ОтменитьТранзакцию(); + РаботаСКоннекторами.ОтменитьТранзакцию(Коннектор); КонецПроцедуры // Возвращает текущий активный коннектор. // // Возвращаемое значение: -// АбстрактныйКоннектор - Возвращает экземпляр коннектора. Конкретная реализация определяется параметром -// ТипКоннектора при вызове конструктора МенеджерСущностей. +// АбстрактныйКоннектор - Возвращает экземпляр коннектора. Конкретная реализация определяется параметром +// ТипКоннектора при вызове конструктора МенеджерСущностей. // Функция ПолучитьКоннектор() Экспорт Возврат Коннектор; КонецФункции +// Получает ХранилищеСущностей, привязанное к переданному типу сущности. +// +// Параметры: +// ТипСущности - Тип - Тип сущности, зарегистрированный в Модели +// +// Возвращаемое значение: +// ХранилищеСущностей - Хранилище сущностей, привязанное к переданному типу сущности. +// +Функция ПолучитьХранилищеСущностей(ТипСущности) Экспорт + ОбъектМодели = МодельДанных.Получить(ТипСущности); + ХранилищеСущностей = ХранилищаСущностей.Получить( + ОбъектМодели, + Коннектор + ); + Возврат ХранилищеСущностей; +КонецФункции + +// @internal +// Для служебного пользования. +// +// Возвращает пул сущностей из хранилища сущностей, привязанного к переданному типу сущности. +// +// Параметры: +// ТипСущности - Тип - Тип сущности, зарегистрированный в Модели. +// +// Возвращаемое значение: +// Соответствие - Пул сущностей. +// +Функция ПолучитьПулСущностей(ТипСущности) Экспорт + Возврат ПолучитьХранилищеСущностей(ТипСущности).ПолучитьПулСущностей(); +КонецФункции + // <Описание процедуры> // // Параметры: // ТипКоннектора - Тип - Тип, проверяемый на реализацию интерфейса // Процедура ПроверитьПоддержкуИнтерфейсаКоннектора(ТипКоннектора) - + ИнтерфейсКоннектор = Новый ИнтерфейсОбъекта; ИнтерфейсКоннектор.ИзОбъекта(Тип("АбстрактныйКоннектор")); РефлекторОбъекта = Новый РефлекторОбъекта(ТипКоннектора); ПоддерживаетсяИнтерфейсКоннектора = РефлекторОбъекта.РеализуетИнтерфейс(ИнтерфейсКоннектор); - + Ожидаем.Что( - ПоддерживаетсяИнтерфейсКоннектора, + ПоддерживаетсяИнтерфейсКоннектора, СтрШаблон("Тип <%1> не реализует интерфейс коннектора", ТипКоннектора) ).ЭтоИстина(); @@ -276,30 +229,12 @@ // ТипКласса - Тип - Тип, в котором проверяется наличие необходимых аннотаций. // Процедура ПроверитьЧтоКлассЯвляетсяСущностью(ТипКласса) - + РефлекторОбъекта = Новый РефлекторОбъекта(ТипКласса); ТаблицаМетодов = РефлекторОбъекта.ПолучитьТаблицуМетодов("Сущность", Ложь); Ожидаем.Что(ТаблицаМетодов, СтрШаблон("Класс %1 не имеет аннотации &Сущность", ТипКласса)).ИмеетДлину(1); - + ТаблицаСвойств = РефлекторОбъекта.ПолучитьТаблицуСвойств("Идентификатор"); Ожидаем.Что(ТаблицаСвойств, СтрШаблон("Класс %1 не имеет поля с аннотацией &Идентификатор", ТипКласса)).ИмеетДлину(1); -КонецПроцедуры - -Процедура ПроверитьЧтоТипСущностиЗарегистрированВМодели(ТипСущности) - ОбъектМодели = МодельДанных.Получить(ТипСущности); - Ожидаем.Что(ОбъектМодели, "Тип сущности не зарегистрирован в модели данных").Не_().Равно(Неопределено); -КонецПроцедуры - -Процедура ПроверитьНеобходимостьЗаполненияИдентификатора(Сущность) - ОбъектМодели = МодельДанных.Получить(Тип(Сущность)); - Если ОбъектМодели.Идентификатор().ГенерируемоеЗначение Тогда - Возврат; - КонецЕсли; - - ЗначениеИдентификатора = ОбъектМодели.ПолучитьЗначениеИдентификатора(Сущность); - Ожидаем.Что( - ЗначениеИдентификатора, СтрШаблон("Сущность с типом %1 должна иметь заполненный идентификатор", Тип(Сущность)) - ).Заполнено(); - -КонецПроцедуры +КонецПроцедуры \ No newline at end of file diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" new file mode 100644 index 0000000..0d1d631 --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\245\321\200\320\260\320\275\320\270\320\273\320\270\321\211\320\265\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" @@ -0,0 +1,122 @@ +#Использовать "../internal" + +Перем ОбъектМодели; +Перем Коннектор; +Перем ПулСущностей; + +Процедура ПриСозданииОбъекта(Знач ПОбъектМодели, Знач ТипКоннектора, Знач СтрокаСоединения, ПараметрыКоннектора) + ОбъектМодели = ПОбъектМодели; + Коннектор = РаботаСКоннекторами.СоздатьКоннектор(ТипКоннектора); + + РаботаСКоннекторами.ОткрытьКоннектор(Коннектор, СтрокаСоединения, ПараметрыКоннектора); + ПулСущностей = Новый Соответствие(); +КонецПроцедуры + +// Сохраняет сущность в БД. +// +// Параметры: +// Сущность - Произвольный - Объект (экземпляр класса, зарегистрированного в модели) для сохранения в БД. +// +Процедура Сохранить(Сущность) Экспорт + РаботаСКоннекторами.Сохранить(Коннектор, ОбъектМодели, ПулСущностей, Сущность); +КонецПроцедуры + + +// Осуществляет поиск сущностей типа, привязанного к ХранилищуСущностей, по идентификатору. +// +// Параметры: +// Отбор - Произвольный - Отбор для поиска. +// Если параметр не задан или равен "Неопределено", то возвращаются все найденные сущности указанного типа. +// Если параметр имеет тип "Соответствие", то каждое значение соответствия преобразуется к условию поиска +// ИмяПоля = ЗначениеПоля, где ИмяПоля - ключ элемента соответствия, ЗначениеПоля - значение элемента соответствия. +// Если параметр имеет тип "Массив", то каждое элемент массива должен иметь тип "ЭлементОтбора". +// Каждый элемент отбора преобразуется к условию поиска. В качестве "ПутьКДанным" указываются имена полей. +// Если параметр имеет тип "ЭлементОтбора", то элемент отбора преобразуется к условию поиска. +// В качестве "ПутьКДанным" указываются имена полей. +// +// Возвращаемое значение: +// Массив - Массив найденных сущностей. В качестве элементов массива выступают +// экземпляры класса с типом, привязанным к ХранилищуСущностей, с заполненными значениями полей. +// +Функция Получить(Отбор = Неопределено) Экспорт + Возврат РаботаСКоннекторами.Получить(Коннектор, ОбъектМодели, ПулСущностей, Отбор); +КонецФункции + +// Осуществляет поиск сущности типа, привязанного к ХранилищуСущностей, по идентификатору. +// +// Параметры: +// Отбор - Произвольный - Отбор для поиска. +// Если параметр не задан или равен "Неопределено", то возвращаются все найденные сущности указанного типа. +// Если параметр имеет тип "Соответствие", то каждое значение соответствия преобразуется к условию поиска. +// ИмяПоля = ЗначениеПоля, где ИмяПоля - ключ элемента соответствия, ЗначениеПоля - значение элемента соответствия. +// Если параметр имеет тип "Массив", то каждое элемент массива должен иметь тип "ЭлементОтбора". +// Каждый элемент отбора преобразуется к условию поиска. В качестве "ПутьКДанным" указываются имена полей. +// Если параметр имеет тип "ЭлементОтбора", то элемент отбора преобразуется к условию поиска. +// В качестве "ПутьКДанным" указываются имена полей. +// Любой другой тип интерпретируется как поиск по &Идентификатору. +// +// Возвращаемое значение: +// Произвольный - Если сущность была найдена, то возвращается экземпляр класса с типом, +// привязанным к ХранилищуСущностей, с заполненными значениями полей. Иначе возвращается "Неопределено". +// +Функция ПолучитьОдно(Знач Отбор = Неопределено) Экспорт + Возврат РаботаСКоннекторами.ПолучитьОдно(Коннектор, ОбъектМодели, ПулСущностей, Отбор); +КонецФункции + +// Выполняет удаление сущности из базы данных. +// Сущность должна иметь заполненный идентификатор. +// +// Параметры: +// Сущность - Произвольный - Удаляемая сущность +// +Процедура Удалить(Сущность) Экспорт + РаботаСКоннекторами.Удалить(Коннектор, ОбъектМодели, ПулСущностей, Сущность); +КонецПроцедуры + +// Выполняет очистку текущего Хранилища сущностей. +// Дополнительно посылает коннектору запрос на закрытие соединения. +// +Процедура Закрыть() Экспорт + РаботаСКоннекторами.ЗакрытьКоннектор(Коннектор); + ПулСущностей.Очистить(); +КонецПроцедуры + +// Посылает коннектору запрос на начало транзакции. +// +Процедура НачатьТранзакцию() Экспорт + РаботаСКоннекторами.НачатьТранзакцию(Коннектор); +КонецПроцедуры + +// Посылает коннектору запрос на фиксацию транзакции. +// +Процедура ЗафиксироватьТранзакцию() Экспорт + РаботаСКоннекторами.ЗафиксироватьТранзакцию(Коннектор); +КонецПроцедуры + +// Посылает коннектору запрос на отмену транзакции. +// +Процедура ОтменитьТранзакцию() Экспорт + РаботаСКоннекторами.ОтменитьТранзакцию(Коннектор); +КонецПроцедуры + +// Возвращает текущий активный коннектор. +// +// Возвращаемое значение: +// АбстрактныйКоннектор - Возвращает экземпляр коннектора. Конкретная реализация определяется параметром +// ТипКоннектора при вызове конструктора МенеджерСущностей. +// +Функция ПолучитьКоннектор() Экспорт + Возврат Коннектор; +КонецФункции + +// @internal +// Для служебного пользования. +// +// Возвращает пул сущностей текущего объекта. +// +// Возвращаемое значение: +// Соответствие - Пул сущностей. +// +Функция ПолучитьПулСущностей() Экспорт + Возврат ПулСущностей; +КонецФункции diff --git a/tasks/coverage.os b/tasks/coverage.os new file mode 100644 index 0000000..9c6d1d1 --- /dev/null +++ b/tasks/coverage.os @@ -0,0 +1,33 @@ +#Использовать 1commands +#Использовать fs +#Использовать coverage + +СистемнаяИнформация = Новый СистемнаяИнформация; +ЭтоWindows = Найти(НРег(СистемнаяИнформация.ВерсияОС), "windows") > 0; + +ФС.ОбеспечитьПустойКаталог("coverage"); +ПутьКСтат = "coverage/stat.json"; + +Команда = Новый Команда; +Команда.УстановитьКоманду("oscript"); +Если НЕ ЭтоWindows Тогда + Команда.ДобавитьПараметр("-encoding=utf-8"); +КонецЕсли; +Команда.ДобавитьПараметр(СтрШаблон("-codestat=%1", ПутьКСтат)); +Команда.ДобавитьПараметр("tasks/test.os"); // Файла запуска тестов +Команда.ПоказыватьВыводНемедленно(Истина); + +КодВозврата = Команда.Исполнить(); + +Файл_Стат = Новый Файл(ПутьКСтат); + +ИмяПакета = "oscript-package"; + +ПроцессорГенерации = Новый ГенераторОтчетаПокрытия(); + +ПроцессорГенерации.ОтносительныеПути() + .ФайлСтатистики(Файл_Стат.ПолноеИмя) + .Cobertura() // Формирование отчета в формате Cobertura + .Сформировать(); + +ЗавершитьРаботу(КодВозврата); \ No newline at end of file diff --git a/tasks/test.os b/tasks/test.os new file mode 100644 index 0000000..996d8d6 --- /dev/null +++ b/tasks/test.os @@ -0,0 +1,27 @@ +#Использовать ".." +#Использовать 1testrunner + +Функция ПрогнатьТесты() + + Тестер = Новый Тестер; + + ПутьКТестам = ОбъединитьПути(ТекущийСценарий().Каталог, "..", "tests"); + ПутьКОтчетуJUnit = ОбъединитьПути(ТекущийСценарий().Каталог, ".."); + + КаталогТестов = Новый Файл(ПутьКТестам); + Если Не КаталогТестов.Существует() Тогда + Сообщить(СтрШаблон("Не найден каталог тестов %1", ПутьКТестам)); + Возврат Истина; + КонецЕсли; + + РезультатТестирования = Тестер.ТестироватьКаталог( + КаталогТестов, + Новый Файл(ПутьКОтчетуJUnit) + ); + + Успешно = РезультатТестирования = 0; + + Возврат Успешно; +КонецФункции // ПрогнатьТесты() + +ПрогнатьТесты(); diff --git "a/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" "b/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" index a4ec40d..26fe091 100644 --- "a/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" +++ "b/tests/\320\234\320\265\320\275\320\265\320\264\320\266\320\265\321\200\320\241\321\203\321\211\320\275\320\276\321\201\321\202\320\265\320\271.os" @@ -3,7 +3,7 @@ Перем МенеджерСущностей; Процедура ПередЗапускомТеста() Экспорт - МенеджерСущностей = Новый МенеджерСущностей(Тип("КоннекторSQLite"), "Data Source=:memory:"); + МенеджерСущностей = Новый МенеджерСущностей(Тип("КоннекторSQLite"), "FullUri=file::memory:?cache=shared"); ПодключитьСценарий(ОбъединитьПути(ТекущийКаталог(), "tests", "fixtures", "Автор.os"), "Автор"); ПодключитьСценарий(ОбъединитьПути(ТекущийКаталог(), "tests", "fixtures", "СущностьБезГенерируемогоИдентификатора.os"), "СущностьБезГенерируемогоИдентификатора"); @@ -263,4 +263,24 @@ КонецПроцедуры +&Тест +Процедура СинхронизацияЭкземпляровСущностей() Экспорт + + Сущность = Новый СущностьСоВсемиТипамиКолонок; + Сущность.Целое = 1; + Сущность.Ссылка = Сущность; + МенеджерСущностей.Сохранить(Сущность); + + Сущность1 = МенеджерСущностей.ПолучитьОдно(Тип("СущностьСоВсемиТипамиКолонок"), 1); + Сущность2 = МенеджерСущностей.ПолучитьОдно(Тип("СущностьСоВсемиТипамиКолонок"), 1); + + Ожидаем.Что(Сущность1, "Ссылки на сущности совпадают").Равно(Сущность2); + + Сущность1.Целое = 2; + Ожидаем.Что(Сущность2.Целое, "Поля сущностей синхронизированы").Равно(Сущность1.Целое); + + Ожидаем.Что(Сущность1.Ссылка, "Сущность ссылается сама на себя").Равно(Сущность1); + +КонецПроцедуры + // TODO: Переписать тесты с проверки на записи в таблице БД на вызов методов поиска, когда они будут реализованы