При разработке сложных механизмов в 1С:Предприятие часто возникает необходимость объединить несколько операций записи объектов БД в единый неделимый блок. Если при выполнении одной из операций происходит ошибка, все ранее внесенные изменения должны отмениться. Для этого используются явные транзакции. Однако неправильное сочетание вызовов НачатьТранзакцию(), ЗафиксироватьТранзакцию() и блока Попытка ... Исключение может привести к непроходимым ошибкам и блокировкам СУБД.
Рассмотрим, к каким проблемам приводит помещение вызова НачатьТранзакцию() внутрь блока Попытка, каков официальный стандарт разработки 1С и как правильно организовывать работу с транзакциями.
Проанализируем шаблон кода, который часто вызывают на обсуждение начинающие разработчики:
Ошибка = Ложь;
Попытка
НачатьТранзакцию();
// Что-то делаем с базой данных
Если НЕ Ошибка Тогда
ЗафиксироватьТранзакцию();
Иначе
ОтменитьТранзакцию();
КонецЕсли;
Исключение
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
// Обработка ошибки
КонецПопытки;
В этой конструкции кроется сразу несколько существенных рисков:
Попытка после такой ошибки произойдет попытка записи других данных или вызов дополнительных функций, 1С вызовет критическое исключение с сообщением о том, что в транзакции уже происходили ошибки.ПриЗаписи), вызов ОтменитьТранзакцию() отменит всю внешнюю транзакцию целиком, а счетчик транзакций сбросится. Попытка внешнего кода завершить свою транзакцию вызовет аварийное завершение работы программы.Ошибка = Истина внутри блока Попытка усложняет код. Блок обработки исключений предназначен именно для того, чтобы перехватывать любые сбои автоматически без ручных проверок.Согласно стандартам разработки платформы 1С:Предприятие, вызов НачатьТранзакцию() должен располагаться строго ПЕРЕД блоком Попытка, а фиксация транзакции — быть ПОСЛЕДНИМ оператором основного блока Попытка. Отмена транзакции должна происходить на первой строчке в секции Исключение.
Разберем эталонный шаблон выполнения транзакционных операций по шагам:
// 1. Подготовительные действия вне транзакции
// (выборка данных, проверки, внешние HTTP-запросы)
НачатьТранзакцию();
Попытка
// 2. Потенциально опасные действия с базой данных
// (запись документов, изменение регистров)
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
// 3. Логирование и обработка ошибки
ЗаписатьОшибкуВЖурналРегистрации(ИнформацияОбОшибке());
ВызватьИсключение; // Или вывод сообщения пользователю
КонецПопытки;
Посмотрим на преимущества этого подхода:
Попытка, управление сразу же передается в секцию Исключение, где первым делом вызывается ОтменитьТранзакцию(), снимая все блокировки СУБД.ЗафиксироватьТранзакцию() гарантированно вызывается только тогда, когда все предыдущие строки кода выполнились успешно и без исключений.Проанализируем ключевые моменты, которые необходимо учитывать при работе с транзакциями в 1С:
НачатьТранзакцию().НачатьТранзакцию() лишь увеличивает внутренний счетчик. Реальная фиксация данных в БД происходит только тогда, когда самый внешний вызов ЗафиксироватьТранзакцию() обнуляет счетчик. При этом один вызов ОтменитьТранзакцию() на любом уровне отменяет абсолютно все изменения всех уровней.В специализированных задачах встречается паттерн, когда необходимо создать объекты базы данных, сформировать по ним движения, прочитать полученные расчетные данные и затем гарантированно откатить изменения обратно, не сохраняя их в базе.
Разберем пример реализации такой схемы:
НачатьТранзакцию();
Попытка
// Создаем временный документ и проводим его
ДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// Заполнение реквизитов...
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
// Получаем необходимые расчетные данные из регистров
// Всегда отменяем транзакцию для отката изменений
ОтменитьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Таким образом, строгое соблюдение порядка вызова функций НачатьТранзакцию() до блока Попытка и ОтменитьТранзакцию() в первой строке секции Исключение обеспечивает стабильную работу конфигурации и предотвращает появление трудноотлаживаемых ошибок СУБД.