IBX, FIBPlus, UIB, ADO, .Net и прочее-прочее-прочее, в общем все, что относится к созданию приложений, работающих с InterBase, Firebird и Yaffil - клиент-серверных, трехзвенных, консольных и т.п.
Модератор: kdv
-
СанЕк
- Сообщения: 25
- Зарегистрирован: 25 окт 2005, 11:45
Сообщение
СанЕк » 25 окт 2005, 11:55
Имееться функция удаления записей из таблици или иная другая
Например
Код: Выделить всё
Function deleteTable(Var Q: TIBSQL; Const tablename, whereline: String;AutoTransiction:boolean=true): boolean;
Begin
if AutoTransiction then begin // авто стартование транзикции
if Q.Transaction.Active then Q.Transaction.Rollback;
Q.Transaction.StartTransaction;
end;
try
result := true;
With q.SQL Do
Begin
// генерация SQL запроса
End;
Try
q.ExecQuery; // выполнение запроса
Except result := false;
IBDataBaseError;
End;
finally
if result then Q.Transaction.Commit else Q.Transaction.Rollback;
end;
End;
Вопрос в следующем:
Хотелось бы знать, кто что думает об такой структуре запроса, какие еще есть методы гарантированного завершения транзикции, стоит ли обрабатывать транзикции при Select запросах.
-
kdv
- Forum Admin
- Сообщения: 6595
- Зарегистрирован: 25 окт 2004, 18:07
Сообщение
kdv » 25 окт 2005, 12:40
1. старайся избегать "автотранзакций".
2. ТАК писать не рекомендую, настоятельно -
Q.Transaction.StartTransaction....
Ты же поименовал компонент transaction? Чего бы прямо не написать
MyTransaction.StartTransaction. А то потом в коде не поймешь, что за транзакция стартовала.
3. блок try/except/finally и в конце commit или rollback - это песня.
тут я даже не знаю, что тебе посоветовать...
4. без нужды звать rollback (в начале кода) - это моветон (неприлично и себе дороже).
-
Владимир Каратаев
- Сообщения: 22
- Зарегистрирован: 01 ноя 2004, 11:11
Сообщение
Владимир Каратаев » 25 окт 2005, 12:45
Код: Выделить всё
procedure deleteTable(Var Q: TIBSQL; Const tablename, whereline: String;AutoTransiction:boolean=true);
Begin
try
if AutoTransiction then begin // авто стартование транзикции
if Q.Transaction.Active then Q.Transaction.Rollback;
Q.Transaction.StartTransaction;
end;
With q.SQL Do
Begin
// генерация SQL запроса
End;
q.ExecQuery; // выполнение запроса
finally
if result then Q.Transaction.Commit else Q.Transaction.Rollback;
end;
End;
1.если ты генерируешь exception, то нет необходимости во флаге успешности выполнения запроса. по-любому, если процедура (потому я и предлагаю вариант с процедурой, а не функцией) выполнилась, то произойдет нормальный выход из нее, иначе- exception.
2.блок finally позволяет откатить изменения в любом случае.
3.обрати внимание, у компонента TIBTransaction есть свойство AllowAutoStart. Если его поставить =true, транзакция будет сама стартовать. может тебе такой вариант подойдет.
если нужен вариант с функцией:
Код: Выделить всё
function deleteTable(Var Q: TIBSQL; Const tablename, whereline: String;AutoTransiction:boolean=true): boolean;
Begin
result:=false;
try
try
if AutoTransiction then begin // авто стартование транзикции
if Q.Transaction.Active then Q.Transaction.Rollback;
Q.Transaction.StartTransaction;
end;
With q.SQL Do Begin
// генерация SQL запроса
End;
q.ExecQuery; // выполнение запроса
Result:=true;
finally
if result then Q.Transaction.Commit else Q.Transaction.Rollback;
end;
except
IBDataBaseError;
end;
End;
ps: прислушайся к советам kdv. в твоем случае они могут и не пригодиться, но на ус мотай.
-
СанЕк
- Сообщения: 25
- Зарегистрирован: 25 окт 2005, 11:45
Сообщение
СанЕк » 25 окт 2005, 13:03
Используеться только потому что функция как бы универсальная и использует ту транзикцию которая присоединена к TIBSQL.
Код: Выделить всё
if Q.Transaction.Active then Q.Transaction.Rollback;
на тот случай если вдруг каким нить образом произошел выход из какой либо процедуры(обновления/добавления/удаления) без завершения транзикции, то есть Commit не произошел, и что бы не выдавалось сообщение о том что транзикция уже стартанула я ее Rollback. Но насчет этого я еще подумаю. Наверное стоит ее убрать всетаки. Пусть юзеры прогу перегружают в таком случае.
А функция это для того что бы можно было самому управлять транзикциями на крайний случай.
Всем спасибо за ценные советы.
-
Dimitry Sibiryakov
- Заслуженный разработчик
- Сообщения: 1436
- Зарегистрирован: 15 сен 2005, 09:05
Сообщение
Dimitry Sibiryakov » 25 окт 2005, 14:07
Кстати, commit тоже может обломаться, так что я рекомендую такой код:
Код: Выделить всё
Transaction.StartTransaction;
try
...
Transaction.Commit;
except
Transaction.Rollback;
raise;
end;
-
Владимир Каратаев
- Сообщения: 22
- Зарегистрирован: 01 ноя 2004, 11:11
Сообщение
Владимир Каратаев » 25 окт 2005, 14:19
Dimitry Sibiryakov писал(а):Кстати, commit тоже может обломаться, так что я рекомендую такой код:
Код: Выделить всё
Transaction.StartTransaction;
try
...
Transaction.Commit;
except
Transaction.Rollback;
raise;
end;
3-й совет kdv- тут Rollback может обломиться. в except лучше вообще не делать ничего, что вызовет еще один except.
-
Dimitry Sibiryakov
- Заслуженный разработчик
- Сообщения: 1436
- Зарегистрирован: 15 сен 2005, 09:05
Сообщение
Dimitry Sibiryakov » 25 окт 2005, 15:18
У Rollback не так уж много шансов для ошибки. Фактически, три:
1) Shared transaction handle
2) Transaction handle == null
3) Проблемы с коннектом при использовании 2PC (только).
-
СанЕк
- Сообщения: 25
- Зарегистрирован: 25 окт 2005, 11:45
Сообщение
СанЕк » 25 окт 2005, 16:08
Совместными усилиями получаем функцию или процедуру, кому что больше нравиться, с
почти 100% завершением рабочей транзикции
Код: Выделить всё
Function deleteTable(. . . .): boolean;
Begin
if AutoTransiction then Q.Transaction.StartTransaction;
try
result := true;
// генерируем запрос
Try
q.ExecQuery;// выполняем
Except
result := false; // запрос обломился
IBDataBaseError;
End;
finally
// если все ОК пробуем вызвать Commit
if result then try Q.Transaction.Commit Except result:=false;end;
// если Commit обломился то
if not result then try Q.Transaction.Rollback;finally end;
end;
End;
Всем спасибо!!
Кому если нужно, могу скинуть модуль с несколькими функциями для динамического создания и выполнения запросов Insert, Update, Delete для Ib/FB. Собственно часть его только что здесь откорректирована
[/code]
-
Владимир Каратаев
- Сообщения: 22
- Зарегистрирован: 01 ноя 2004, 11:11
Сообщение
Владимир Каратаев » 25 окт 2005, 16:39
[quote="СанЕк"]Совместными усилиями получаем функцию или процедуру, кому что больше нравиться, с
почти 100% завершением рабочей транзикции
Код: Выделить всё
Function deleteTable(. . . .): boolean;
Begin
if AutoTransiction then Q.Transaction.StartTransaction;
try
result := true;
// генерируем запрос
Try
q.ExecQuery;// выполняем
Except
result := false; // запрос обломился
IBDataBaseError;
End;
finally
// если все ОК пробуем вызвать Commit
if result then try Q.Transaction.Commit Except result:=false;end;
// если Commit обломился то
if not result then try Q.Transaction.Rollback;finally end;
end;
End;
Эх, Санек, нифига ты так и не понял...
Написал самую сбойную функцию, какую можно придумать. я ж тебе выше дал готовый правильный вариант функции. используй ее. Так будет еще правильнее:
Код: Выделить всё
function deleteTable(Var Q: TIBSQL; Const tablename, whereline: String;AutoTransiction:boolean=true): boolean;
Begin
result:=false;
try
try
if AutoTransiction then begin // авто стартование транзикции
if Q.Transaction.Active then Q.Transaction.Rollback;
Q.Transaction.StartTransaction;
end;
With q.SQL Do Begin
// генерация SQL запроса
End;
q.ExecQuery; // выполнение запроса
finally
if result then Q.Transaction.Commit else Q.Transaction.Rollback;
end;
Result:=true;
except
IBDataBaseError;
end;
End;
-
kdv
- Forum Admin
- Сообщения: 6595
- Зарегистрирован: 25 окт 2004, 18:07
Сообщение
kdv » 25 окт 2005, 17:15
я бы еще упростил, объясняю почему:
startTransaction не имеет смысла обрамлять try/except/finally, потому что если она НЕ стартанула, то один фиг дальше в коде процедуры делать нечего, и ошибка спокойно может уйти "наверх", если ее, конечно, не надо обрабатывать по месту (чего мы тут в упор не видим).
то есть
Код: Выделить всё
StartTransaction
try
ExecQuery
except
rollback
end
Commit;
и то, если в execSQL один-единственный оператор (и это не вызов селективной процедуры), то спокойно можно написать
Код: Выделить всё
StartTransaction
try
ExecQuery
finally
Commit
end;
это здесь уже было в каком то топике....
-
СанЕк
- Сообщения: 25
- Зарегистрирован: 25 окт 2005, 11:45
Сообщение
СанЕк » 26 окт 2005, 11:35
Тут у меня еще такое мнение есть по поводу
Код: Выделить всё
StartTransaction
try
ExecQuery
except
rollback
end
Commit;
В этом случае при возникновении ошибки все ОК, но в программе выскочит сообщение и выполнение процедуры (если я не ошибаюсь), в которой была вызвана функция с этим кодом остановиться, например в обработчике FormClose вызывает DeleteTable - возникает ошибка и получаеться что окно нельзя закрыть. Поэтому я всетаки придерживаюсь такого кода
Код: Выделить всё
StartTransaction
try
try
ExecQuery
except
rollback
result:=false // говорит о том что произошла ошибка
end
finally
Commit;
result:=true // все тип топ
end;
-
kdv
- Forum Admin
- Сообщения: 6595
- Зарегистрирован: 25 окт 2004, 18:07
Сообщение
kdv » 26 окт 2005, 11:42
например в обработчике FormClose вызывает DeleteTable
ужас какой. я не знаю, что ты там и как пишешь, но по отрывочным впечатлениям это, извини, какой то кошмар. У тебя явные проблемы с алгоритмикой.
-
СанЕк
- Сообщения: 25
- Зарегистрирован: 25 окт 2005, 11:45
Сообщение
СанЕк » 26 окт 2005, 11:53
Ну этож пример, с алгоритмикой проблем у меня нет
, просто сложно все на пальцах обяснять. пока вроде все работает, и работает хорошо, а это главное.
-
Владимир Каратаев
- Сообщения: 22
- Зарегистрирован: 01 ноя 2004, 11:11
Сообщение
Владимир Каратаев » 26 окт 2005, 11:54
В этом случае при возникновении ошибки все ОК, но в программе выскочит сообщение и выполнение процедуры (если я не ошибаюсь), в которой была вызвана функция с этим кодом остановиться, например в обработчике FormClose вызывает DeleteTable - возникает ошибка и получаеться что окно нельзя закрыть. Поэтому я всетаки придерживаюсь такого кода
Код: Выделить всё
StartTransaction
try
try
ExecQuery
except
rollback
result:=false // говорит о том что произошла ошибка
end
finally
Commit;
result:=true // все тип топ
end;
еще раз повторяю, как будет правильно:
Код: Выделить всё
function deleteTable(Var Q: TIBSQL; Const tablename, whereline: String;AutoTransiction:boolean=true): boolean;
Begin
result:=false;
try
if AutoTransiction then begin // авто стартование транзикции
if Q.Transaction.Active then Q.Transaction.Rollback;
Q.Transaction.StartTransaction;
end;
try
With q.SQL Do Begin
// генерация SQL запроса
End;
q.ExecQuery; // выполнение запроса
Result:=true;
finally
if result then Q.Transaction.Commit else Q.Transaction.Rollback;
end;
except
Result:=false;
IBDataBaseError;
end;
End;
твой вариант кода-
полная бредятина. функция у тебя всегда будет возращать true, даже в случае ошибки, а делфи при компиляции тебе напишет предупреждение, что результат функции не определен. у меня пальцы устанут набирать текст- сколько ты потенциальных глюков посадил в свой код. поэтому говорю последний раз- бери тот код, который я тебе уже третий раз даю. это готовый рабочий код. неужели так трудно его просто скопировать к себе и использовать?