При выполнении групповых операций с данными часто возникает необходимость гарантировать принцип «все или ничего» (атомарность) и одновременно защитить считываемые и изменяемые данные от параллельного редактирования другими сеансами. В платформе 1С существует несколько механизмов блокировок и работы с транзакциями, которые выполняют разные задачи. Разберем подробно, как правильно накладывать блокировки на конкретные элементы справочников или документов и как не допустить ошибок при сбоях.
Для обеспечения принципа «все или ничего» используются транзакции СУБД. Все изменения, выполняемые между вызовами НачатьТранзакцию() и ЗафиксироватьТранзакцию(), либо успешно сохраняются в базе данных целиком, либо полностью отменяются при вызове ОтменитьТранзакцию().
Проанализируем ситуацию, когда в ходе выполнения кода возникает ошибка. Если отмена транзакции происходит во встроенном блоке обработки исключений, крайне важно перевызывать исключение с помощью команды ВызватьИсключение, чтобы вышестоящий код узнал об откате транзакции.
Вызов метода Заблокировать() у конкретного объекта (например, СправочникОбъект или ДокументОбъект) накладывает пессимистическую (сеансовую) блокировку. Разберем особенности её работы:
Если необходимо заблокировать конкретный объект или запись на уровне базы данных в рамках выполняемой транзакции, применяется объект БлокировкаДанных. Вопреки распространенному заблуждению, его можно накладывать не только на всю таблицу метаданных целиком, но и на конкретные ссылки.
Посмотрим на пример кода, который блокирует конкретную ссылку справочника:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Полуфабрикаты");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаПолуфабрикат);
Блокировка.Заблокировать();
Рассмотрим ключевые моменты работы с БлокировкаДанных:
Управляемый. Если объект находится в автоматическом режиме, явный вызов метода Заблокировать() вызовет ошибку, если при старте транзакции не был явно передан режим РежимУправленияБлокировкойДанных.Управляемый.БлокировкаДанных можно добавить несколько элементов для разных таблиц и заблокировать их за один вызов метода Заблокировать().Рассмотрим готовый пример корректного выполнения манипуляций с данными в единой транзакции с установкой управляемой блокировки:
НачатьТранзакцию();
Попытка
// 1. Создаем и настраиваем управляемую блокировку
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Полуфабрикаты");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНаОбъект);
// 2. Устанавливаем блокировку до чтения и изменения данных
Блокировка.Заблокировать();
// 3. Выполняем действия с объектом
Объект = СсылкаНаОбъект.ПолучитьОбъект();
Если Объект <> Неопределено Тогда
// Выполняем необходимые изменения
Объект.Записать();
КонецЕсли;
// 4. Фиксируем транзакцию
ЗафиксироватьТранзакцию();
Исключение
// 5. В случае ошибки отменяем транзакцию и пробрасываем ошибку дальше
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Выясним причину особенностей работы в файловом режиме: в файловой базе платформа использует логический менеджер управляемых блокировок для защиты записей, однако на уровне файла базы данных блокируется вся таблица целиком. При переходе на клиент-серверный вариант (например, MS SQL или PostgreSQL) управляемые блокировки обеспечивают высокую параллельность работы, блокируя только нужные строки на время транзакции.