Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/МенеджерСущностей.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,14 @@
// @internal
// Для служебного пользования.
//
// Возвращает пул сущностей из хранилища сущностей, привязанного к переданному типу сущности.
// Возвращает пул сущностей текущего потока исполнения из хранилища сущностей, привязанного к
// переданному типу сущности.
//
// Параметры:
// ТипСущности - Тип - Тип сущности, зарегистрированный в Модели.
//
// Возвращаемое значение:
// Соответствие - Пул сущностей.
// Соответствие - Пул сущностей текущего потока.
//
Функция ПолучитьПулСущностей(ТипСущности)
```
Expand Down
32 changes: 32 additions & 0 deletions docs/ПотокобезопаснаяРаботаСБД.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,38 @@ PostgreSQL это закрывает параметр строки соедин
Прежний `ПолучитьКоннектор()` удален: он отдавал соединение наружу, не имея способа получить его
обратно, поэтому соединение оставалось за контекстом навсегда.

### Экземпляры сущностей

Прочитанные сущности хранилище запоминает в пуле сущностей, и у каждого контекста исполнения пул
свой. Внутри контекста строка читается одним и тем же экземпляром и между вызовами: повторное
чтение того же идентификатора заполняет поля этого экземпляра заново, а ссылки других сущностей
указывают на него же. Разные контексты получают разные экземпляры одной строки, поэтому чтение в
одном не стирает несохраненные правки другого и не смешивает снимки строки, прочитанные в разное
время.

```bsl
// Основной поток
Автор = МенеджерСущностей.ПолучитьОдно(Тип("Автор"), 1);
Автор.Имя = "Правка";

// Фоновое задание в это же время
АвторЗадания = МенеджерСущностей.ПолучитьОдно(Тип("Автор"), 1); // Свой экземпляр, правку не трогает
```

Новый контекст создает прочитанные экземпляры заново. Это дешево: тип активной записи
регистрируется один раз на класс сущности, и экземпляр создается из него без компиляции модуля.

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

Сохранения пул не согласует. Если два контекста (или два процесса) изменили одну строку каждый в
своем экземпляре, в БД остается сохраненное последним: версию строки библиотека не проверяет.

Пул контекста растет, пока контекст жив, - у основного потока долгоживущего процесса до закрытия
менеджера. `ХранилищеСущностей.Закрыть()` и `МенеджерСущностей.Закрыть()` отбрасывают прочитанные
экземпляры во всех контекстах: пул закрывающего освобождается сразу, остальных - при их следующем
обращении к хранилищу или по завершении контекста.

### Закрытие

`ИсточникДанных.Закрыть()` закрывает свободные соединения и соединения закрывающего контекста
Expand Down
9 changes: 6 additions & 3 deletions docs/ХранилищеСущностей.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@
```bsl
// Выполняет очистку текущего Хранилища сущностей.
//
// Очищает ранее прочитанные экземпляры: следующая операция читает сущность из БД заново.
// Отбрасывает ранее прочитанные экземпляры во всех потоках исполнения: следующая операция читает
// сущность из БД в новый экземпляр. Пул текущего потока освобождается сразу, пулы других потоков -
// при их следующем обращении к хранилищу или по завершении потока.
//
Процедура Закрыть() Экспорт
```
Expand Down Expand Up @@ -179,10 +181,11 @@
// @internal
// Для служебного пользования.
//
// Возвращает пул сущностей текущего объекта.
// Возвращает пул сущностей текущего потока исполнения: прочитанные им экземпляры по
// идентификаторам. Пул, заведенный до очистки хранилища, отбрасывается и заводится заново.
//
// Возвращаемое значение:
// Соответствие - Пул сущностей.
// Соответствие - Пул сущностей текущего потока.
//
Функция ПолучитьПулСущностей() Экспорт
```
Expand Down
2 changes: 1 addition & 1 deletion packagedef
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
.ЗависитОт("fs", "1.2.0")
.ЗависитОт("lambdas", "0.3.4")
.ЗависитОт("logos", "1.7.1")
.ЗависитОт("decorator", "3.0.0")
.ЗависитОт("decorator", "3.1.1")
.ЗависитОт("extends", "0.2.0")
.ЗависитОт("reflector", "0.7.1")
.ЗависитОт("sql", "1.3.3")
Expand Down
96 changes: 80 additions & 16 deletions src/internal/Модули/АктивнаяЗапись.os
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#Использовать decorator
#Использовать reflector

Функция ТипСущности(Сущность) Экспорт
ТипСущности = ТипЗнч(Сущность);
Если ТипСущности = Тип("Сценарий") Тогда
ТипСущности = ОбработкаДекоратора.ИсходныйТип(Сущность);
КонецЕсли;
// Соответствие - Тип сущности: имя зарегистрированного типа ее активной записи.
Перем ТипыЗаписей;

// БлокировкаРесурса - Защищает ТипыЗаписей и регистрацию типов: модуль общий на процесс.
Перем Блокировка;

Перем Рефлектор;

Возврат ТипСущности;
Функция ТипСущности(Сущность) Экспорт
Возврат ОбработкаДекоратора.ИсходныйТип(Сущность);
КонецФункции

// Создает экземпляр сущности, расширенный методами паттерна Active Record.
Expand All @@ -15,6 +19,12 @@
// поэтому в методы записи его передавать не нужно, а операцию хранилище все равно делегирует
// своему менеджеру.
//
// Экземпляр создается из типа активной записи, зарегистрированного один раз на класс сущности:
// компилировать модуль на каждый экземпляр дорого, а экземпляры заново создает каждый поток
// исполнения. Тип общий у всех менеджеров процесса, поэтому хранилище и модель проставляются
// экземпляру после создания, а не значениями полей по умолчанию: те остались бы в реестре типов
// навсегда и держали бы хранилище первого менеджера.
//
// Параметры:
// ОбъектМодели - ОбъектМодели - Модель сущности, экземпляр которой создается.
// ХранилищеСущностей - ХранилищеСущностей - Хранилище типа этой сущности.
Expand All @@ -23,32 +33,86 @@
// Произвольный - Экземпляр класса сущности с методами Прочитать, Сохранить и Удалить.
//
Функция Создать(ОбъектМодели, ХранилищеСущностей) Экспорт
Сущность = Новый(ОбъектМодели.ТипСущности());

Декоратор = Новый ПостроительДекоратора(Сущность)
Сущность = Новый(ТипЗаписи(ОбъектМодели.ТипСущности()));

Рефлектор.УстановитьСвойство(Сущность, "_ХранилищеСущностей", ХранилищеСущностей);
Рефлектор.УстановитьСвойство(Сущность, "_ОбъектМодели", ОбъектМодели);

Возврат Сущность;

КонецФункции

// Возвращает имя типа активной записи класса сущности, при первом обращении регистрируя его.
//
// Параметры:
// ТипСущности - Тип - Класс сущности.
//
// Возвращаемое значение:
// Строка - Имя зарегистрированного типа.
//
Функция ТипЗаписи(Знач ТипСущности)

Блокировка.Заблокировать();

Попытка

ИмяТипа = ТипыЗаписей.Получить(ТипСущности);
Если ИмяТипа = Неопределено Тогда
ИмяТипа = ЗарегистрироватьТипЗаписи(ТипСущности);
ТипыЗаписей.Вставить(ТипСущности, ИмяТипа);
КонецЕсли;

Исключение
Ошибка = ИнформацияОбОшибке();
Блокировка.Разблокировать();
ВызватьИсключение;
КонецПопытки;

Блокировка.Разблокировать();

Возврат ИмяТипа;

КонецФункции

// Регистрирует тип активной записи класса сущности под уникальным в процессе именем.
//
// Параметры:
// ТипСущности - Тип - Класс сущности.
//
// Возвращаемое значение:
// Строка - Имя зарегистрированного типа.
//
Функция ЗарегистрироватьТипЗаписи(Знач ТипСущности)

ИмяТипа = "АктивнаяЗапись_" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");

Построитель = Новый ПостроительДекоратора(ТипСущности)
.Импорт(Новый Импорт("decorator"))

.Поле(Новый Поле("_ХранилищеСущностей")
.ЗначениеПоУмолчанию(ХранилищеСущностей))
.Поле(Новый Поле("_ХранилищеСущностей"))

.Поле(Новый Поле("_ОбъектМодели")
.ЗначениеПоУмолчанию(ОбъектМодели))
.Поле(Новый Поле("_ОбъектМодели"))

.Метод(Новый Метод("Прочитать")
.Публичный()
.ТелоМетода("_ДанныеСущности = _ХранилищеСущностей.ПолучитьОдно(_ОбъектМодели.ПолучитьЗначениеИдентификатора(ЭтотОбъект));
|ОбработкаДекоратора.СинхронизироватьПоля(_ДанныеСущности, ЭтотОбъект);"))


.Метод(Новый Метод("Сохранить")
.Публичный()
.ТелоМетода("_ХранилищеСущностей.Сохранить(ЭтотОбъект);"))

.Метод(Новый Метод("Удалить")
.Публичный()
.ТелоМетода("_ХранилищеСущностей.Удалить(ЭтотОбъект);"))
.ТелоМетода("_ХранилищеСущностей.Удалить(ЭтотОбъект);"));

.Построить();
Построитель.ЗарегистрироватьВСистемеТипов(ИмяТипа);

Возврат ИмяТипа;

Возврат Декоратор;
КонецФункции

ТипыЗаписей = Новый Соответствие;
Блокировка = Новый БлокировкаРесурса;
Рефлектор = Новый Рефлектор;
Loading
Loading