Страница 1 из 2
Отменить изменения
Добавлено: 09 апр 2007, 17:31
santilaas
Привет всем!!!
Firebird 1.5.3, FibPlus 5.2
Вопрос такой:
- нужно, чтобы, если в блоке Try произошла ошибка, то данные никуда не записывались и выполнился код except, - у меня же даже при ошибке (скажем произошедшей после DM.DataSet.Post;) происходит запись в Датасет - вот код:
Код: Выделить всё
DM.DS_A_User2.Transaction.StartTransaction;
try
SecurityService1.AddUser;
DM.DataSet.Post;
//еще кое-какие операции
DM.DataSet.Transaction.CommitRetaining;
FForm.ModalResult:= mrOK;
except
Application.MessageBox(PChar('Ошибка при добавлении пользователя!' + #13 +
' Обратитесь к администратору!'),'Ошибка приложения!',MB_OK+MB_ICONERROR);
DM.DataSet.Cancel;
DM.DataSet.Transaction.RollbackRetaining;
end
- что здесь неправильно? мож чего не так с транзакциями (кстати, AutoCommit поставил в False)?
Заранее благодарен
Добавлено: 09 апр 2007, 18:10
WildSery
CommitRetaining - дурной тон.
Может ошибка в "FForm.ModalResult:= mrOK;"
Может коммит где-то был в "//еще кое-какие операции"
И, кстати, а где собственно DM.DataSet.Transaction.StartTransaction?
Добавлено: 10 апр 2007, 18:10
santilaas
при постановке вопроса я немного ошибся - вот так выглядит код:
Код: Выделить всё
DM.DataSet.Append;
DM.DataSet.Transaction.StartTransaction;
try
SecurityService1.AddUser;
DM.DataSet.Post;
//еще кое-какие операции
DM.DataSet.Transaction.CommitRetaining;
FForm.ModalResult:= mrOK;
except
Application.MessageBox(PChar('Ошибка при добавлении пользователя!' + #13 +
' Обратитесь к администратору!'),'Ошибка приложения!',MB_OK+MB_ICONERROR);
DM.DataSet.Cancel;
DM.DataSet.Transaction.RollbackRetaining;
end
- меня интересует, вообще мой подход правильный или нет?
Добавлено: 10 апр 2007, 23:18
kdv
в корне неверно.
управление пользователями (Services API) никак не связано с транзакциями. То есть, вначале надо try добавить пользователя, а потом, если все прошло Ok, уже заниматься всякими Post и другими операциями с базой.
А то, ты небось еще не знаешь, что пользователи не в БД хранятся

Ну и про commitRetaining и тем более rollbackretaining уже сказали.
Добавлено: 11 апр 2007, 07:51
Dimitry Sibiryakov
А я еще добавлю про Append перед StrartTransaction. Не то, чтобы совсем уж "так делать нельзя", но... нехорошо это. Может обрушиться в самый неподходящий момент.
Добавлено: 11 апр 2007, 18:30
santilaas
В общем с транзакцией я все разрулил - надо было сделать так:
у DataSet-а ставим такие свойства:
"Options" - "poStartTransaction" = "False";
"AutoCommit" = "True"
и юзаем транзакцию на запись "Tr_Write"
Сам код такой:
Код: Выделить всё
DM.Tr_Write.StartTransaction;
DM.DataSet.Append;
try
SecurityService1.AddUser;
DM.DataSet.Post;
//еще кое-какие операции
//если не произошло ошибок, подтверждаем транзакцию
if DM.Tr_Write.InTransaction then DM.Tr_Write.Commit;
FForm.ModalResult:= mrOK;
except
DM.DataSet.Cancel;
//при ошибке откатываем транзакцию
DM.Tr_Write.Rollback;
Application.MessageBox(PChar('Ошибка при добавлении пользователя!' + #13 +
' Обратитесь к администратору!'),'Ошибка приложения!',MB_OK+MB_ICONERROR);
end
- по крайней мере так у меня все работает на ура.
а насчет замечания про Post я приму к сведению - спасибо за советы
Добавлено: 12 апр 2007, 02:07
kdv
забодал. ну порнуха ведь полная. нетранзакционные действия смешаны с транзакционными. Например. AddUser проходит, а Post вызывает exception. И что у тебя по роллбэку отменится? НИЧЕГО. Юзер останется добавленным, а действие по Post все равно не прошло.
И с автокоммитом код получился еще более хреновым. Короче, чешуя...
Добавлено: 12 апр 2007, 04:32
CyberMax
Код: Выделить всё
DM.DataSet.Post;
//еще кое-какие операции
//если не произошло ошибок, подтверждаем транзакцию
if DM.Tr_Write.InTransaction then DM.Tr_Write.Commit;
Неверно. У тебя AutoCommit = True, а это значит, что в Post пишущая транзакция будет автоматически подтверждена. Таким образом, в проверке DM.Tr_Write.InTransaction будет всегда False.
Код: Выделить всё
DM.DataSet.Cancel;
//при ошибке откатываем транзакцию
DM.Tr_Write.Rollback;
Неверно. Cancel никакого отношения к WriteTransaction не имеет, так как происходит всего лишь удаление буфера под новую запись и возврат к состоянию dsBrowse. Поэтому RollBack бессмысленен.
Код: Выделить всё
Application.MessageBox(PChar('Ошибка при добавлении пользователя!' + #13 +
' Обратитесь к администратору!'),'Ошибка приложения!',MB_OK+MB_ICONERROR);
Ты в курсе про MessageBox?
Добавлено: 15 апр 2007, 19:17
santilaas
В общем, по каждому пункту конкретно:
управление пользователями (Services API) никак не связано с транзакциями. То есть, вначале надо try добавить пользователя, а потом, если все прошло Ok, уже заниматься всякими Post и другими операциями с базой
AddUser проходит, а Post вызывает exception. И что у тебя по роллбэку отменится? НИЧЕГО. Юзер останется добавленным, а действие по Post все равно не прошло
- не спорю, но как тогда поступить в моем случае: я при нажатии на кнопку хочу, чтобы пользователь добавлялся и в таблицу в БД "security.fdb" (через SecurityService1.AddUser) и в мою таблицу "Users" в моей БД и чтобы этот юзер включался в определенную роль (grant 'role' to 'user'), притом, если происходит ошибка при добавлении - в любую таблицу - мне нужно, чтобы отменялась операция полностью (т.е. данные не добавились ни туда, ни сюда). Как это реализовать? - решение же должно быть - хотя опять же я понимаю, что grant я смогу сделать только после того, как добавится моя запись в таблицу БД "security.fdb" - как в таком случае выйти из ситуации?
И с автокоммитом код получился еще более хреновым
AutoCommit поставил в False
Ты в курсе про MessageBox?
а что с ним то не так?
У меня просьба - если не трудно - кто-нибудь, поправьте, пожалуйста, мой код, исходя из поставленной задачи - а то я чувствую я еще долго здесь буду зависать?!

Добавлено: 16 апр 2007, 01:57
CyberMax
santilaas писал(а):Ты в курсе про MessageBox?
а что с ним то не так?
Он несколько неудобен. В модуле Dialogs есть хороший метод MessageDlg.
Добавлено: 16 апр 2007, 01:57
CyberMax
santilaas писал(а):Ты в курсе про MessageBox?
а что с ним то не так?
Он несколько неудобен. В модуле Dialogs есть хороший методы для работы с боксами, например, MessageDlg.
Добавлено: 16 апр 2007, 16:30
santilaas
в добавление к моему предыдущему ответу - у меня как всегда масса вопросов - хочу добить эти транзакции раз и навсегда:
1) вопрос еще такой - если у меня 2 транзакции (Tr_Read и Tr_Write), то при ручном управлении транзакциями нужно писать так:
Код: Выделить всё
DM.DataSet.UpdateTransaction.StartTransaction; //здесь для DataSet-а UpdateTransaction поставлена в Tr_Write
или так
или это без разницы?
2) и потом надо ли вообще активизировать транзакцию для записи Tr_Write сразу после коннекта к БД или просто делать
StartTransaction по мере необходимости? -
3) если поставлю для всех DataSet-ов свойство poStartTransaction в True (в инспекторе объектов Delphi), то можно ли будет уже при кодировании не писать
DataSet.Transaction.StartTransaction и будет ли это правильным?
4)
Код: Выделить всё
if DM.DataSet.UpdateTransaction.InTransaction then
DM.DataSet.UpdateTransaction.Commit;
- обязательно ли здесь делать проверку на InTransaction?
5) добавление записи заключаю в блок try ... except --- вопрос в том, надо или нет в except откатывать транзакцию или нет - какой из вариантов правильный:
или
Код: Выделить всё
if DM.DataSet.UpdateTransaction.InTransaction then
DM.DataSet.UpdateTransaction.Rollback;
DM.DataSet.Cancel;
если не трудно ответьте, пожалуйста, на каждый вопрос
Добавлено: 16 апр 2007, 16:42
kdv
1.
то при ручном управлении транзакциями нужно писать так
пофиг. собственно, здесь просто удобство и область действия Tr_write. Если она привязана только как updatetransaction, то необязательно ее звать по имени.
2.
и потом надо ли вообще активизировать транзакцию для записи Tr_Write сразу после коннекта к БД или просто делать
StartTransaction по мере необходимости?
в фибплюсах, если я правильно помню, update transaction по умолчанию стартует само, где быстро выполняется оператор изменения, и тут же вызывается commitretaining. который опциями надо заменить на commit. После чего можно updatetransaction вообще не трогать.
3. см. пункт 2 по поводу UpdateTransaction.
Для всех остальных транзакций это НЕПРАВИЛЬНО.
я так и не пойму, ты ibx.htm читал про транзакции?
4. см. пункт 2.
5. сначала операции с датасетом, потом с транзакцией, к которой он привязан.
Кроме того, Cancel у DataSet это альтернатива Post. Соответственно, зачем тут rollback, если dataset.post не вызывался?
Добавлено: 17 апр 2007, 02:43
CyberMax
kdv писал(а):2.
и потом надо ли вообще активизировать транзакцию для записи Tr_Write сразу после коннекта к БД или просто делать
StartTransaction по мере необходимости?
в фибплюсах, если я правильно помню, update transaction по умолчанию стартует само, где быстро выполняется оператор изменения, и тут же вызывается commitretaining. который опциями надо заменить на commit. После чего можно updatetransaction вообще не трогать.
Давайте разберем этот момент.
В TpFIBDataSet две транзакции:
1) Постоянно открытая читающая (Transaction).
2) Короткая ("быстрая") пишущая (UpdateTransaction).
Существует две настройки для управления стартом и подтверждением транзакций.
1. Опция poStartTransaction (в Options). Используется для читающей и пишущей транзакций. Предназначена для автозапуска (при необходимости) транзакции, если до того она была неактивна.
Пример: Transaction неактивна. Набор закрыт.
Вызываем pFIBDataSet.Open.
При poStartTransaction автоматически выполнится Transaction.StartTransaction и набор откроется, иначе будет возбуждено исключение о неактивной транзакции.
Пример: вызван оператор pFIBDataSet.Post. Так как изменения происходят в контексте пишущей транзакции, то она должна быть активна. При poStartTransaction UpdateTransaction будет автоматически запущена. При выключенной опции ее надо вручную стартовать.
2. Свойство AutoCommit. Относится только к UpdateTransaction. Эта транзакция может быть любой, в том числе и ReadOnly

. В последнем случае вы просто не сможете подтверждить изменения и будут возбуждаться соответствующие исключения.
Это свойство предназначено для автоматического вызова UpdateTransaction.Commit (не CommitRetaining) после каждого Post и Delete.
Есть еще одно свойство - RefreshTransaction, которое принимает два значения: tkReadTransaction и tkUpdateTransaction. Оно указывает, в какой транзакции делать обновления записи. Для чего это надо: допустим, вы работаете со справочником, в котором изменения подтверждаются только после нажатия кнопки "Сохранить". При tkReadTransaction после refresh'а записи получаешь версию записи до изменения (изменения "исчезнут"), а при tkUpdateTransaction - измененную.
Таким образом, при poStartTransaction и AutoCommit (а также при правильных настройках транзакций) получаем автоматически срабатывающий механизм обновления записей, который может работать бесконечно долго без ущерба для производительности.
P.S. Читающая транзакция обычно одна на все датасеты и стартует сразу же после подключения к базе, коммитясь при выходе из приложения.
Добавлено: 17 апр 2007, 10:01
kdv
Эта транзакция может быть любой, в том числе и ReadOnly. В последнем случае вы просто не сможете подтверждить изменения и будут возбуждаться соответствующие исключения.
более точно - commit/rollback для readonly можно сделать всегда, вот любые операторы, изменяющие БД, не пройдут в такой транзакции.
Добавлено: 17 апр 2007, 10:55
CyberMax
kdv писал(а):Эта транзакция может быть любой, в том числе и ReadOnly. В последнем случае вы просто не сможете подтверждить изменения и будут возбуждаться соответствующие исключения.
более точно - commit/rollback для readonly можно сделать всегда, вот любые операторы, изменяющие БД, не пройдут в такой транзакции.
Там "подтвердить изменения" были в смысле отпостить, а не откоммитить

. Виноват, неправильно высказался.
Добавлено: 17 апр 2007, 17:53
santilaas
смотрю интересная беседа получается - кстати, спасибо за развернутые ответы - многое мне стало понятным., но кое-что надо еще переварить, а потом комментировать. А ibx.htm я читал, но там не все описано, что мне нужно.
И все-таки:
управление пользователями (Services API) никак не связано с транзакциями. То есть, вначале надо try добавить пользователя, а потом, если все прошло Ok, уже заниматься всякими Post и другими операциями с базой
AddUser проходит, а Post вызывает exception. И что у тебя по роллбэку отменится? НИЧЕГО. Юзер останется добавленным, а действие по Post все равно не прошло
- не спорю, но как тогда поступить в моем случае: я при нажатии на кнопку хочу, чтобы пользователь добавлялся и в таблицу в БД "security.fdb" (через SecurityService1.AddUser) и в мою таблицу "Users" в моей БД и чтобы этот юзер включался в определенную роль (grant 'role' to 'user'), притом, если происходит ошибка при добавлении - в любую таблицу - мне нужно, чтобы отменялась операция полностью (т.е. данные не добавились ни туда, ни сюда). Как это реализовать? - решение же должно быть - хотя опять же я понимаю, что grant я смогу сделать только после того, как добавится моя запись в таблицу БД "security.fdb" - как в таком случае выйти из ситуации?
А MessageBox я юзаю, т.к. он заточен под русский язык - и в принципе мне он никаких проблем не доставляет , наоборот, очень даже неплохо
Добавлено: 17 апр 2007, 23:09
kdv
надо еще переварить, а потом комментировать. А ibx.htm я читал, но там не все описано, что мне нужно.
интересно, что же там может быть "не описано"?
- не спорю, но как тогда поступить в моем случае: я при нажатии на кнопку хочу, чтобы пользователь добавлялся и в таблицу в БД "security.fdb" (через SecurityService1.AddUser) и в мою таблицу "Users" в моей БД и чтобы этот юзер включался в определенную роль (grant 'role' to 'user'), притом, если происходит ошибка при добавлении - в любую таблицу - мне нужно, чтобы отменялась операция полностью
надо понять сначала, что AddUser это нетранзакционный вызов. Т.е. управлять транзакциями при вызове функций Services API и некоторых операторов DDL невозможно (или они не подчиняются транзакциям или им невозможно сделать rollback). И следовательно, если AddUser выполнилось, а последующая взаимосвязанная операция не прошла, то надо
а) отменять взаимосвязанную операцию, допустим по rollback
б) удалять пользователя, если "транзакцией" является И AddUser, И эта "взаимосвязанная операция".
Добавлено: 18 апр 2007, 18:22
santilaas
и потом надо ли вообще активизировать транзакцию для записи Tr_Write сразу после коннекта к БД или просто делать
StartTransaction по мере необходимости?
- я ведь почему задал такой вопрос - я не могу понять одного: вроде как пишущую транзакцию нежелательно долго держать открытой,
а если я уже при коннекте её активизирую, то пока я доберусь до датасета, пройдет некоторое время (понимаю, что пример не ахти какой, но все же). Поэтому, ещё раз спрашиваю: в FIB-ах надо ли мне при коннекте активировать (стартовать) пишущую транзакцию или достаточно активировать её при открытии датасетов (poStartTransaction = True) - как правильнее?
update transaction по умолчанию стартует само, где быстро выполняется оператор изменения, и тут же вызывается commitretaining. который опциями надо заменить на commit. После чего можно updatetransaction вообще не трогать.
- где это такое и какими опциями можно изменить?
сначала операции с датасетом, потом с транзакцией, к которой он привязан. Кроме того, Cancel у DataSet это альтернатива Post. Соответственно, зачем тут rollback, если dataset.post не вызывался?
- ну дело в том, что как я понимаю, запущенную транзакцию (например, у меня poStartTransaction у DataSet-а стоит в True) надо
обязательно закрыть, т.е. либо подтвердить, либо откатить, или я не прав?
Есть еще одно свойство - RefreshTransaction, которое принимает два значения: tkReadTransaction и tkUpdateTransaction.
- это про RefreshTransactionKind?
Оно указывает, в какой транзакции делать обновления записи. Для чего это надо: допустим, вы работаете со справочником, в котором изменения подтверждаются только после нажатия кнопки "Сохранить". При tkReadTransaction после refresh'а записи получаешь версию записи до изменения (изменения "исчезнут"), а при tkUpdateTransaction - измененную.
-не совсем понял, зачем нам получать неизмененные данные?
(а также при правильных настройках транзакций)
- CyberMax, какие настройки являются правильными для тебя, на основе твоего опыта разработки приложений БД?
удалять пользователя, если "транзакцией" является И AddUser, И эта "взаимосвязанная операция".
- так скорее всего и сделаю.
Добавлено: 18 апр 2007, 20:54
kdv
я не могу понять одного: вроде как пишущую транзакцию нежелательно долго держать открытой
ага, потому что она будет удерживать потенциальный мусор от сборки
Поэтому, ещё раз спрашиваю: в FIB-ах надо ли мне при коннекте активировать (стартовать) пишущую транзакцию или достаточно активировать её при открытии датасетов (poStartTransaction = True) - как правильнее?
я начинаю впадать в ступор. Какой нафиг старт транзакции при коннекте? ЗАЧЕМ? Зачем тебе ДЛИННЫЕ ПИШУЩИЕ ТРАНЗАКЦИИ? Ладно бы если бы они были нужны на самом деле. Зачем они тебе лично? Более того, тебе же показали пример, когда UpdateTransaction стартует и завершается в совершенно нужный момент, т.е. при Post ?
я не пойму - ты в транзакции вообще не врубаешься, или конкретно в управление ими в FIBPlus?
Если второе, то
http://www.devrace.com/ru/fibplus/articles/2291.php
Если первое, то статей на ibase.ru достаточно, в частности я уже наверное второй или третий раз даю посыл в
http://www.ibase.ru/devinfo/ibx.htm#tran_use