Шпора по транзакциям на языке 1С - Программист 1С Минск. Автоматизация бизнеса.

Перейти к контенту

Шпора по транзакциям на языке 1С

Шпаргалка по статье Сергея Старых (tormozit) о работе транзакций в 1С:Предприятии 8.3: https://infostart.ru/1c/articles/1026771/

Главная идея: важно различать платформенную транзакцию 1С, транзакцию СУБД и счётчик вложенности транзакций; ошибка внутри транзакции может не только вызвать исключение, но и пометить текущую транзакцию как неотменяемую для дальнейшей работы («откат только»).
Ключевое: наличие блока Попытка…Исключение не означает, что после ошибки можно безопасно работать дальше в той же транзакции. Сначала нужно проверить её состояние и, если она испорчена, отменить.
Обычный безопасный каркас: начать транзакцию, выполнить операцию, зафиксировать; при исключении - отменить и повторно пробросить ошибку. Не стоит без необходимости вызывать ОтменитьТранзакцию() в обработчике, если вы не знаете, была ли транзакция начата именно в текущем методе. Метод может выполняться внутри внешней транзакции вызывающего кода.
Базовый шаблон:
НачатьТранзакцию();
Попытка
   // Работа в транзакции
   ЗафиксироватьТранзакцию();
Исключение
   ОтменитьТранзакцию();
   // Если ошибку надо передать выше
   ВызватьИсключение;
КонецПопытки;
Ошибка не всегда равна простому исключению. Некоторые ошибки становятся критичными для текущей транзакции. Например:
  • ошибка SQL-запроса;
  • ошибка записи объекта;
  • блокировка или конфликт конкурентного доступа;
  • ошибка проведения/записи, связанная с обращением к данным;
  • отдельные ошибки платформы и СУБД.
После такой ошибки транзакция может быть помечена как неотменяемая. Даже если исключение обработано, последующие обращения к данным, запись объектов или фиксация могут завершиться сообщением: В данной транзакции уже происходили ошибки.

Обычный безопасный каркас:
начать транзакцию, выполнить операцию, зафиксировать; при исключении - отменить и повторно пробросить ошибку.
Проверка состояния:
// Метод для проверки транзакции "В данной транзакции уже происходили ошибки" (работает на глобальном контексте)
Функция ПроверитьОтменуТранзакции() Экспорт
   Запрос = Новый Запрос("ВЫБРАТЬ 1");
   Попытка
       Запрос.Выполнить();
       Возврат Ложь;
   Исключение
       Возврат Истина;
   КонецПопытки;
КонецФункции
Проверяем, можно ли ещё продолжать работу.
Не передавайте объект ошибки «как есть»:
// Получаем информацию об ошибке и без исключения в транзакции проверяем отменяемость транзакции с помощью глобальной функции "В данной транзакции уже происходили ошибки"
// Переходим в метод ИнформацияОбОшибке() можно не выходя из транзакции и передаем в модуль менеджера, если информация была получена в нем.
// Функция в методе модуля менеджера объекта ИнформацияОбОшибке. Использовать эту функцию только в том случае если исключение в транзакции обрабатывается в модуле менеджера.
Функция ИнформацияОбОшибкеБезИсключения(Ошибка) Экспорт
   Если ПроверитьОтменуТранзакции() Тогда
       Результат = "" + Ошибка.ПодробноеОписание();
   Иначе
       Результат = Ошибка;
   КонецЕсли;
   Возврат Результат;
КонецФункции
И использование:
Исключение
   ИнформацияОбОшибке();
   ИнформацияОбОшибке("Ошибка", ИнформацияОбОшибкеКлиента.Ошибка, ОписаниеОшибкиБезИсключения(Ошибка));
   //...
   ВызватьИсключение;
КонецПопытки;
Смысл: до логирования и передачи ошибки убедиться, что сама операция обработки ошибки не вызовет новую проблему в уже повреждённой транзакции.

Вложенные транзакции
Вложенность в 1С - это прежде всего работа со счётчиком транзакций, а не полноценные независимые SQL-транзакции уровня SAVEPOINT.
  • Первый НачатьТранзакцию() открывает транзакцию.
  • Вложенный вызов увеличивает счётчик.
  • Внутренняя фиксация уменьшает счётчик, но реальная фиксация в СУБД произойдёт только при возврате счётчика к нулю.
  • Внутренняя отмена также влияет на общий контекст; она не создаёт автономную точку отката для внутреннего метода.
  • Если внешний метод уже начал транзакцию, внутренний метод не должен бездумно завершать её так, будто она принадлежит ему.
Практическое правило: метод должен понимать, он владелец транзакции или лишь работает в транзакции вызывающего кода.

Безопасный внутренний метод
Если метод может запускаться как самостоятельно, так и внутри уже открытой транзакции, полезно:
  1. Определять, есть ли активная транзакция.
  2. Открывать свою транзакцию только при необходимости.
  3. При ошибке откатывать только ту транзакцию, за жизненный цикл которой отвечает этот метод.
  4. Не скрывать критическую ошибку от вызывающего кода.

Логика выглядит так:
  • Нет внешней транзакции → метод начинает, фиксирует или отменяет свою.
  • Есть внешняя транзакция → метод не должен неожиданно фиксировать весь внешний сценарий.
  • Ошибка сделала общую транзакцию неотменяемой → вызывающий уровень должен получить информацию об ошибке и корректно выполнить откат.
Пример, ошибка запроса:
Процедура ТестОшибкаВТранзакции1() Экспорт
   НачатьТранзакцию(); // Счетчик = 1
   Попытка
       Ссылка = Справочники.Номенклатура.Элемент1;
       НаименованиеСтрокой = "" + Ссылка; // Получаем строку для передачи в обработчик исключения. Она может быть преобразована функцией в дто.
       НачатьТранзакцию(); // Счетчик = 2
       Попытка
           Запрос = Новый Запрос("ВЫБРАТЬ 1/0");
           Запрос.Выполнить();
           ЗафиксироватьТранзакцию(); // Счетчик = 2
       Исключение
           ОтменитьТранзакцию(); // Счетчик = 2
           //ИнформацияОбОшибке("Ошибка", ИнформацияОбОшибкеКлиента.Ошибка, Ошибка); // Так получим дополнительную ошибку, если информация была получена в дто
           ИнформацияОбОшибке("Ошибка", ИнформацияОбОшибкеКлиента.Ошибка, ИнформацияОбОшибкеБезИсключения(Ошибка));
           Сообщить("Произошла ошибка дто - элемент справочника """ + НаименованиеСтрокой + """");
           ВызватьИсключение;
       КонецПопытки;
       ЗафиксироватьТранзакцию(); // Счетчик = 1
   Исключение
       ОтменитьТранзакцию(); // Счетчик = 1
       Сообщить("Произошла ошибка дто");
       ВызватьИсключение;
   КонецПопытки;
КонецПроцедуры
Что здесь важно запомнить:
  • Вложенная транзакция не изолирует ошибку от внешней.
  • Ошибка ВЫБРАТЬ 1/0 делает продолжение работы рискованным.
  • Ошибку надо передавать так, чтобы не вызвать новый сбой.
  • Верхний уровень должен корректно отменить свой контекст.infostart

Если ошибка произошла при записи объекта, ситуация обычно опаснее, чем при чисто вычислительной ошибке: платформа уже могла начать операции с данными и блокировками.
Ошибка записи объекта:
НачатьТранзакцию();
Попытка
    Объект = Справочники.Номенклатура.Элемент1.ПолучитьОбъект();
    Объект.Наименование = Строка(Дата());
    Объект.Записать();
    Запрос = Новый Запрос("ВЫБРАТЬ 1/0");
    Запрос.Выполнить(); // Ошибка меняет флаг отменяемости транзакции
Исключение
    Сообщить("Произошла ошибка дто");
    ВызватьИсключение;
КонецПопытки;
ЗафиксироватьТранзакцию(); // Ошибка отменяемости транзакции, т.к. счетчик равен 0 и флаг = Истина
Если после ошибки выполнить ЗафиксироватьТранзакцию(), можно получить ошибку фиксации: текущая транзакция уже признана некорректной. Поэтому после критической ошибки нужен ОтменитьТранзакцию(), а не попытка довести выполнение до штатной фиксации.

Для сценариев записи важно после перехвата исключения проверять, не испорчена ли транзакция:
Обработка при записи:
Процедура ТестЗапись(Отказ)
   Объект.Наименование = Строка(Дата());
   О = Справочники.Номенклатура.Элемент1.ПолучитьОбъект();
   О.Реквизит1 = Строка(Дата());
   О.Записать();
   Попытка
       Запрос = Новый Запрос("ВЫБРАТЬ 1/0");
       Запрос.Выполнить(); // Ошибка меняет флаг отменяемости транзакции
   Исключение
   КонецПопытки;
   Если Транзакция1.ПроверитьОтменяемостьТранзакции() Тогда
       ОтменитьТранзакцию(); // Уменьшаем счетчик транзакции. Так как он равен 0 и изменен флаг отмены, транзакция отменяется.
       // Передаем ошибку пользователю с отмененной транзакцией. ИнформацияОбОшибке получает объект ошибки и вышла.
   КонецЕсли;
   // Записываем дто отмененной транзакции
   О = Справочники.Номенклатура.Элемент1.ПолучитьОбъект();
   О.Наименование = Строка(Дата());
   О.Записать();
   // Проверяем флаг и не записываем, т.к. транзакция дто отменена
   Если Не Транзакция1.ПроверитьОтменяемостьТранзакции() Тогда
       ЗафиксироватьТранзакцию(); // Счетчик выйдет из вложенной транзакции, пока внешняя транзакция делает запись
   КонецЕсли;
КонецПроцедуры
Если ошибка сделала транзакцию неотменяемой, её необходимо завершить откатом до выполнения действий, которые требуют рабочей транзакции: повторной записи, регистрации в логах, формирования служебных данных и т. п.

Эти подходы сохраняют целостность данных и уменьшают число дефектов.
0
комментарии
____________________
Copyright©, «Программист 1С в г.Минске», 22.09.2026
Перепечатка текста и фотографий разрешена при наличии прямой ссылки на источник
Яндекс.Метрика
Назад к содержимому