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