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: Переписать тесты с проверки на записи в таблице БД на вызов методов поиска, когда они будут реализованы