Профессия — 1С

Рукопашный бой Карташ

Категории

Транзакция в попытке

рубрики: Язык программирования 1С | Дата: 25 Апрель, 2017

В прошлый раз мы рассмотрели простейший способ явной организации транзакций средствами встроенного языка 1С. На практике транзакции гораздо чаще используются совместно с конструкцией Попытка — Исключение. Это позволяет в случае ошибки продолжить выполнение кода, а также выдать адекватное сообщение об ошибке пользователю и записать информацию в журнал регистрации или в файл логов для последующего анализа администратором системы.




Рекомендации фирмы 1С

Если мы обратимся к технической документации или к диску ИТС, то увидим, что фирма 1С рекомендует следующий способ организации транзакции в попытке




Попытка

	//1. Начало транзакции.
	НачатьТранзакцию();

	//2. Блок операций, выполняющихся в транзакции.

	//3. Если все операции успешны, фиксируем транзакцию.
	ЗафиксироватьТранзакцию();

Исключение

	//4. Если при выполнении кода возникли ошибки, отменяем транзакцию.
	ОтменитьТранзакцию();

	//5. При необходимости запись в журнал регистрации.
	//6. При необходимости вывод сообщения пользователю.

КонецПопытки;

Собственно каких-то особых пояснений код не требует. Если в процессе попытки выполнения транзакционного кода возникает ошибка, мы сразу проваливаемся в блок исключение, т.е. до метода ЗафиксироватьТранзакцию() мы просто не доходим. Ну а в исключении соответственно отменяем транзакцию и если это необходимо выводим сообщение об ошибке и записываем информацию в журнал регистрации. Фиксировать ошибки в журнале регистрации крайне желательно, особенно для тех операций, которые выполняются без участия пользователя (например, регламентные задания). Это позволит в дальнейшем проанализировать ошибку. Вместо записи в журнал регистрации можно организовать отправку сообщений администратору по электронной почте.




Теперь вооружившись новыми знаниями попробуем видоизменить код, рассмотренный в статье про простейшие транзакции. Напомню, что мы рассматривали запись в справочник Товары и в регистр сведений Цена по следующей схеме:




&НаСервереБезКонтекста
Процедура ВыполнитьТранзакциюНаСервере()

	НачатьТранзакцию();

	//записываем новый товар
	Товар = Справочники.Товары.СоздатьЭлемент();
	Товар.Наименование = "Дырокол";
	Товар.Записать();

	//записываем цену
	НаборЗаписей = РегистрыСведений.Цена.СоздатьНаборЗаписей();

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

	НаборЗаписей.Записать();

	ЗафиксироватьТранзакцию();

КонецПроцедуры

А теперь поместим транзакцию в блок Попытка Исключение. Скорее всего ошибки могут возникнуть только в момент записи в справочник или в регистр сведений, поэтому предварительную подготовку вынесем за пределы транзакции.





&НаСервереБезКонтекста
Процедура ВыполнитьТранзакциюНаСервере()

	//создаем новый товар
	Товар = Справочники.Товары.СоздатьЭлемент();
	Товар.Наименование = "Дырокол";

	//Создаем запись с ценой
	НаборЗаписей = РегистрыСведений.Цена.СоздатьНаборЗаписей();

	НоваяЗапись = НаборЗаписей.Добавить();
	НоваяЗапись.Период	= ТекущаяДата();
	НоваяЗапись.Сумма	= 100;

	//Выполняем транзакцию в попытке
	Попытка

		НачатьТранзакцию();

		Товар.Записать();

		НоваяЗапись.Товар	= Товар.Ссылка;
		НаборЗаписей.Записать();

		ЗафиксироватьТранзакцию();

	Исключение

		ОтменитьТранзакцию();

		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "Произошла ошибка при записи товара и его цены";
		Сообщение.Сообщить();

		ЗаписьЖурналаРегистрации("Произошла ошибка при записи товара и его цены");

	КонецПопытки;

КонецПроцедуры

Как НЕ НАДО делать

У тех кто только начинает работать с транзакциями зачастую возникает желание сделать вот таким образом




НачатьТранзакцию();

Попытка
	НачатьТранзакцию();
	//Блок операций
	ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
КонецПопытки;

Попытка
	НачатьТранзакцию();
	//Блок операций
	ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
КонецПопытки;

ЗафиксироватьТранзакцию();

Или в цикле




НачатьТранзакцию();

Для каждого Данные Из МассивДанных Цикл

	Попытка
		НачатьТранзакцию();
		Данные.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
	    ОтменитьТранзакцию();
	КонецПопытки;

КонецЦикла;

ЗафиксироватьТранзакцию();

На первый взгляд мы сделали все в соответствии с рекомендациями фирмы 1С. Но дело в том, что платформа 1С не поддерживает вложенные транзакции. То есть чисто технически так писать можно. Но при этом все вложенные транзакции не образуют новые, а относятся к той же самой транзакции верхнего уровня. Таким образом, если в одной из вложенных транзакций произойдет ошибка, следующую вложенную транзакцию нельзя будет зафиксировать. Система выдаст сообщение вида: «В данной транзакции уже происходили ошибки!». Продемонстрируем это на примере. Допустим мы решили записать два товара, каждый в своей транзакции. И сделаем эти транзакции вложенными в третью. Далее искусственно вызовем ошибку в первой транзакции с помощью метода ВызватьИсключение:




&НаСервереБезКонтекста
Процедура ВыполнитьТранзакциюНаСервере()

	НачатьТранзакцию();

	Попытка

		НачатьТранзакцию();

		Товар = Справочники.Товары.СоздатьЭлемент();
		Товар.Наименование = "Стол";
		Товар.Записать();

		ВызватьИсключение "Ошибка записи товара.";

		ЗафиксироватьТранзакцию();

	Исключение

		ОтменитьТранзакцию();

		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = ОписаниеОшибки();
		Сообщение.Сообщить();

	КонецПопытки;

	Попытка

		НачатьТранзакцию();

		Товар = Справочники.Товары.СоздатьЭлемент();
		Товар.Наименование = "Стул";
		Товар.Записать();

		ЗафиксироватьТранзакцию();

	Исключение

		ОтменитьТранзакцию();

		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = ОписаниеОшибки();
		Сообщение.Сообщить();

	КонецПопытки;

	ЗафиксироватьТранзакцию();

КонецПроцедуры

В результате выполнения этой процедуры увидим в окне сообщений следующее:


{ВнешняяОбработка.ТранзакцииВПопытке.Форма.Форма.Форма(20)}: Ошибка записи товара.
{ВнешняяОбработка.ТранзакцииВПопытке.Форма.Форма.Форма(40)}: Ошибка при вызове метода контекста (Записать): В данной транзакции уже происходили ошибки!

Таким образом, организация вложенных транзакций в 1С абсолютно бессмысленна.

Возможные варианты

Теперь вернемся к варианту, где мы записывали товар и цену для него. Если у нас при выполнении транзакции произойдет ошибка, то будет трудно понять в какой момент она произошла — при записи товара или при записи цены, поскольку и то и другое происходит в рамках одной попытки. Чтобы определить место возникновения ошибки, нам надо каждую операцию записи заключить в свою собственную попытку и при этом избежать вложенных транзакций. Для этого введем булеву переменную Отказ и в зависимости от ее значения в конце всех операций будем фиксировать или отменять транзакцию.




&НаСервереБезКонтекста
Процедура ВыполнитьТранзакциюНаСервере()

	// Начинаем транзакцию
	Отказ = Ложь;
	НачатьТранзакцию();

	// Пытаемся записать товар
	Попытка

		Товар = Справочники.Товары.СоздатьЭлемент();
		Товар.Наименование = "Дырокол";
		Товар.Записать();

	Исключение

		Отказ = Истина;

		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "Ошибка при записи товара";
		Сообщение.Сообщить();

	КонецПопытки;

	// Пытаемся записать цену
	Попытка

		НаборЗаписей = РегистрыСведений.Цена.СоздатьНаборЗаписей();

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

		НаборЗаписей.Записать();

	Исключение

		Отказ = Истина;

		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "Ошибка при записи цены";
		Сообщение.Сообщить();

	КонецПопытки;

	// Фиксируем или отменяем транзакцию
	Если НЕ Отказ Тогда
		ЗафиксироватьТранзакцию();
	Иначе
		ОтменитьТранзакцию();
	КонецЕсли;

КонецПроцедуры

Аналогичным образом можно поступить и когда мы перебираем и записываем какие-либо данные в цикле. В этом случае мы сможем получить перечень всех данных с ошибками, если такие будут.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

   

2018г. Профессия — 1С. Обмен опытом по программированию в 1С