Механизм транзакций

Статья на ИТС

Транзакция – это последовательность действий, переводящая базу данных из одного целостного состояния в другое целостное состояние. 

Свойства транзакции:

Особенности транзакции:

Из особенностей следуют правила:

Проверка понимания.

Определение и граница использования транзакции. Транзакции - нечто неделимое, возвращающее все в предыдущее состояние. Это осталось в голове после заумных текстов без тестов лапками. Первым, что пришло в голову - восстановить значение реквизита :) Обработка с полем ввода ТестовоеЧисло, 0 по умолчанию.

&НаСервере
Процедура БезОбработкиОшибокНаСервере()
	НачатьТранзакцию();
	ТестовоеЧисло = 5;
	ВызватьИсключение "Число не то!";
	ЗафиксироватьТранзакцию();
КонецПроцедуры

&НаКлиенте
Процедура БезОбработкиОшибок(Команда)
	БезОбработкиОшибокНаСервере();
КонецПроцедуры

Не удалось! Ошибка сгенерировалась, но ТестовоеЧисло стало 5. Ведь транзакции актуальны при изменении данных в базе, т е SQL запрос обрамляется чем-то типа "BEGIN ... COMMIT". Мой косяк, поехали дальше.

Ошибка при создании элемента справочника. Справочник Города. 

&НаСервереБезКонтекста
Процедура НовыйЭлементСправочникаНаСервере()
	НачатьТранзакцию();
	НовыйГород = Справочники.Города.СоздатьЭлемент();
	НовыйГород.Наименование = "Тестовый город";
	НовыйГород.Записать();
    НовыйГород2 = Справочники.Города.СоздатьЭлемент();
	НовыйГород2.Наименование = "Тестовый город 2";
	НовыйГород2.Записать();
	ВызватьИсключение "Город не правильный!";
	ЗафиксироватьТранзакцию();
КонецПроцедуры

Да, тестовые города не создались. Получилось! 

Вложенные транзакции (или то чего нет). 

&НаСервереБезКонтекста
Процедура ВложеннаяТранзакция()
	НачатьТранзакцию();
	Попытка
		НовыйГород = Справочники.Города.СоздатьЭлемент();
		НовыйГород.Наименование = "Тестовый город вложенный";
		НовыйГород.Записать(); 
		ВызватьИсключение "Город не правильный!";
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
	КонецПопытки;
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ВложенныеТарнзакцииНаСервере()
	НачатьТранзакцию();
	НовыйГород = Справочники.Города.СоздатьЭлемент();
	НовыйГород.Наименование = "Тестовый город внешний";
	НовыйГород.Записать(); 
	ВложеннаяТранзакция();
	ЗафиксироватьТранзакцию();   
КонецПроцедуры

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

&НаСервереБезКонтекста
Процедура ВложеннаяТранзакция()
	НачатьТранзакцию();
	Попытка
		НовыйГород = Справочники.Города.СоздатьЭлемент();
		НовыйГород.Наименование = "Тестовый город вложенный";
		НовыйГород.Записать(); 
		ВызватьИсключение "Город не правильный!";
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
	КонецПопытки;
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ВложенныеТарнзакцииНаСервере()
	НачатьТранзакцию();
	Попытка
		НовыйГород = Справочники.Города.СоздатьЭлемент();
		НовыйГород.Наименование = "Тестовый город внешний";
		НовыйГород.Записать(); 
		ВложеннаяТранзакция();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		Сообщить("Исключение было поймано");
	КонецПопытки;
КонецПроцедуры

Вполне логично, что сообщение не было сформировано.

Проверка функции ТранзакцияАктивна. 

&НаСервереБезКонтекста
Процедура НовыйЭлементСправочникаНаСервере() 
	Сообщить(ТранзакцияАктивна());
	НачатьТранзакцию();
	НовыйГород = Справочники.Города.СоздатьЭлемент();
	НовыйГород.Наименование = "Тестовый город 3";
	НовыйГород.Записать(); 
	Сообщить(ТранзакцияАктивна());
	ЗафиксироватьТранзакцию();    
	Сообщить(ТранзакцияАктивна());
КонецПроцедуры

Ожидаемый вывод: Нет, Да, Нет. Теперь проверим факт из статьи о блокировках о возникновении блокировки и как следствие транзакции при чтении (это так я понял текст) 

&НаСервереБезКонтекста
Процедура ТранзакцияПриЧтенииНаСервере()
	МойГород = Справочники.Города.НайтиПоНаименованию("Иркутск");
	Сообщить(ТранзакцияАктивна());
	НовыйГород = Справочники.Города.СоздатьЭлемент();
	НовыйГород.Наименование = "Тестовый город 3";
	НовыйГород.Записать();
	Сообщить(ТранзакцияАктивна());
КонецПроцедуры

&НаКлиенте
Процедура ТранзакцияПриЧтении(Команда)
	ТранзакцияПриЧтенииНаСервере();
КонецПроцедуры

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

&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
	Отказ = ТранзакцияАктивна(); 
КонецПроцедуры

И да, создать объект не удалось, значит транзакция была активна!

Количество выполненных действий при исключении во вложенной транзакции. Смоделируем ситуацию: при входе во вложенную транзакцию происходит подавленная ошибка. Причем только одна ошибка в середине процесса. Сколько раз выполнится внешняя транзакция? Ведь в одном из предыдущих примеров исключение из вложенной транзакции поймано не было. 

&НаСервереБезКонтекста
Процедура ВложеннаяТранзакция()
	НачатьТранзакцию();
	Попытка
		НовыйЭлемент = Справочники.ДляТранзакций.СоздатьЭлемент();
		НовыйЭлемент.Наименование = "Тестовый элемент внутренний";
		НовыйЭлемент.Записать();
		ВызватьИсключение "Ошибка!";
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		Сообщить("Ошибка во вложенной транзакции");
	КонецПопытки;
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ТестТранзакцииНаСервере()
	НачатьТранзакцию();
	Попытка
		Для сч = 1 По 5 Цикл
			НовыйЭлемент = Справочники.ДляТранзакций.СоздатьЭлемент();
			НовыйЭлемент.Наименование = "Тестовый элемент внешний " + Строка(сч);
			НовыйЭлемент.Записать(); 
			Сообщить("Попытка записи " + Строка(сч) + " внешнего элемента.");     
			//Если сч = 3 Тогда
			//	ВложеннаяТранзакция(); //ошибка в середине
			//КонецЕсли;
		КонецЦикла;  
		ВложеннаяТранзакция();//ошибка в конце
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		Сообщить("Исключение было поймано");
	КонецПопытки;
КонецПроцедуры

Сначала все логично. В указанном варианте (он повторяет предыдущий эксперимент) выведено 5 сообщений и "Ошибка во вложенной транзакции". Сообщение "Исключение было поймано" не отображается. 

А дальше ждал сюрприз. Если раскомментировать условие (ошибка на третьем шаге) то получается какая-то бабуйня: выведено 3 сообщения, сообщение "Ошибка во вложенной транзакции" и сообщение "Исключение было поймано"! То есть исключение транслируется наверх, если происходит между выполняемыми действиями. Однако если после выполняемых действий, но до фиксации транзакции - ошибка не транслируется наверх, а транзакция тихо откатывается.

 

 


Revision #5
Created 12 February 2025 13:58:14 by Admin
Updated 13 February 2025 03:47:20 by Admin