Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Транзакции [D7, Любая]


Страницы:   1 2

parovoZZ ©   (2006-04-16 21:40:00) 

Есть следующее:
  try
   ADOConnection.BeginTrans;
   ADOCmd.Execute ();

  except
   ADOConnection.RollbackTrans;

  end;
   ADOConnection.CommitTrans;


Так вот допустим, что во время выполнения операции произошёл обрыв связи с сервером. Куда тогда уйдёт инструкция Rollback?

И потом: обязательно ли явно объявлять транзакцию на операнд SELECT?


SergP   (2006-04-22 10:37:00) [1]


> Так вот допустим, что во время выполнения операции произошёл
> обрыв связи с сервером. Куда тогда уйдёт инструкция Rollback?
>


Уйдет в никуда. Да и если произошел обрыв, то при следующем соединении ты не увидишь изменений сделанных в предыдущий раз, если транзакция не была подтвержджена. Т.е. обрыв - это своего рода тоже некий RollBack. ИМХО.


Sergey Masloff   (2006-04-22 10:26:00) [2]

sniknik ©   (22.04.06 10:09) [27]
>ADOConnection.Execute('BEGIN TRANSACTION Bron');
На мой взгляд лучше. Но я почти не работал с ADO а все компоненты работы с ораклом с которыми я работаю делают именно так как нужно (аналог твоего BEGIN TRANSACTION  с параметрами) так что я не истина в какой-либо инстанции.


sniknik   (2006-04-22 10:09:00) [3]

> 1) между except и end хоть что-то черкнуть не забывай.
... ну это я просто "отсек лишнее" в предыдушем примере, выкинул оттуда ненужное для "моего" сценария, а самом деле и там в предыдушем обработки ошибки нет, тоже не хорошо так писать. (просто тут получилось виднее, блок остался пустым ;)

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

тут у меня возражений то и не было, возражение было против ADOConnection.BeginTrans;, самой формы. хотя бы просто для однотипности написания в програме/ах, ну раз в одном месте пишем одним вариантом, и невозможно использовать второй, то почемубы второй вообще не использовать если он легко заменяется первым?. т.е. почему не написать
ADOCommand.CommandText:= 'BEGIN TRANSACTION Bron';
ADOCommand.Execute;
или если уж экономить строки...
ADOConnection.Execute('BEGIN TRANSACTION Bron');

не лучше ???

тем более это не одно и тоже, есть возможность добавить имя/маркировку транзакции, предварительно установить уровень изоляции. явно ее стартануть... (BeginTrans не стартует явно, судя по профайлеру, оно просто говорит чтобы не закрывалась автоматическая, для одной команды, все последующие уже в ее контексте выполняются.) не знаю как, но такое "смешение стилей" возможно может привести к путанице, чтото останется неподтвержденным... с потерей данных (потом будет "чудо!" все работает, но иногда перезагружаю комп последние данные исчезли...). ну это конечно из области предположений, фантазии, чтобы проверить надо использовать да еще и долго, но это именно то почему это мне не нравится, изза таких вот домыслов, и почему я этим пользоваться не буду. (и другим не советую. тем более альтернатива не сложнее)


Sergey Masloff   (2006-04-22 04:47:00) [4]

Блин привет полный, горло болит спать не могу :(

sniknik ©   (22.04.06 00:16) [22]
Давай тебе пример;-)

что будет в "моем" варианте, старт/комит внутри в команде
try
ADOCmd.Execute ();
//пакет "ушол" на сервер (время посылки помним - мизер),
//выполняется, на 3й "отваливается" сеть... ну и что, пакет то выполняется сервером, он упешно доработает и подтвердит транзакцию...
except
end;

1) между except и end хоть что-то черкнуть не забывай. Я понимаю что в любом реальном коде ты так не напишешь. Я понимаю что и Джо это понимает. Но мало ли неокрепших умов последуют примеру буквально. Лучше перестраховаться ИМХО
2) Все прекрасно. Только если я как клиент отвалюсь а ты там для меня все сделал на сервере, то это все прекрасно только я об этом не узнаю. Че делать-то мне. Я бронировал билет. Отвалился. Теперь я сижу и думаю - а что там было-то? Бронировать мне новый? Или есть у меня уже?

В начальном подходе тут все ясно - забронировал я получил результат и дал коммит. Коммит прошел - усе, я спокойно иду пить водку сделал дело гуляй смело.

Так что ситуации разные бывают когда и твой способ хорош когда и по-другому не помешает.


Джо   (2006-04-22 01:02:00) [5]

> [24] sniknik ©   (22.04.06 00:59)
> про "единственно верный" это ты сам придумал, перечитай
> что я говорил на самом деле еще раз...

Да ничего я не придумал. То есть, в том смысле, что я не приписывал тебе высказывание "о единственно верном пути". Я таким образом хотел подчеркнуть старую истину об известном овоще :)


sniknik   (2006-04-22 00:59:00) [6]

> Но, опять же, как единственно верный совет "писать всегда так" принять не могу :)
про "единственно верный" это ты сам придумал, перечитай что я говорил на самом деле еще раз...


Джо   (2006-04-22 00:51:00) [7]

> [21],[22] sniknik ©

С некоторыми соображениями согласен. Но, опять же, как единственно верный совет "писать всегда так" принять не могу :)
Несколько соображений.

(1) Вот, скажем, ты говоришь, что в качестве альтернативы можно использовать родной SQL-вский скрипт "в первом/последнем ADOCommand". Но это противоречит тому, что ты всячески пытаешься предостеречься от обрывов связи. То есть, один из твоих доводов-таки отпадает, ибо при обрыве связи безразлично было ли ADOConnection.BeginTrans или скрипт в отдельном ADOCmd.

(2) Вернусь к своим баранам :) Упрощая, сейчас имею следующую ситуацию (абстрактный псевдокод):

Connection.BeginTrans;
// весь блок ОБЯЗАН выполниться в единой транзакции
res1 := Command.Execute (ХП1);
res2 := LibraryRoutine (res1);
Command.Execute (ХП(res2));
Connection.CommitTrans;

Как видим, запихнуть это в один скрипт не выйдет. Запихнуть это в одну ХП на сервер тоже не выйдет. Ситуацию с переписыванием LibraryRoutine на языке хранимой процедуры даже и не рассматриваю: допустим (а это так и есть), она сложна, ресурсоемка и это заняло бы слишком много времени.

Если кому интересно, попинайте, пожалуйста, мой подход. Что касается меня, то я не вижу в нем особого криминала, нормальных альтернативных решений не вижу тоже. Заранее могу согласиться с тем, что из-за недостатка опыта я изобретаю велосипеды и горожу невесть что :)


sniknik   (2006-04-22 00:16:00) [8]

нда... [19]

ну давай на примере (это для Джо).
Есть следующее: //пусть есть
ADOConnection.BeginTrans; //стартовали... время посылки команды/пакета мизерное, не учитываем
try
 ADOCmd.Execute (); //пакет "ушол" на сервер, и выполняется, ну скажем 5 мин, на 3й "отвалилась" сеть
except
 ADOConnection.RollbackTrans; //придет сюда по ошибке, и снова "обломится" сети то нет...
end;
ADOConnection.CommitTrans; //и опять "облом".... уже с выдачей сообщения, подтверждения нет...

что будет в "моем" варианте, старт/комит внутри в команде
try
 ADOCmd.Execute ();
 //пакет "ушол" на сервер (время посылки помним - мизер),
 //выполняется, на 3й "отваливается" сеть... ну и что, пакет то выполняется сервером, он упешно доработает и подтвердит транзакцию...
except
end;

выбирай. какой вариант развития событий тебе "ближе и родней"?


sniknik   (2006-04-22 00:00:00) [9]

> (1) они тебе не нравятся
это самая важная причина на самом деле. субьективное ошущение данное в реальности на подсознательном уровне бытия... (почти шутка ;)

насчет [4] [5], ну вообще вначеле описана ситуация с единственным вызовом (Execute) команды/пакета... т.е. разделять искуственно еще и на "транзакционные" команды когда можно все в один обьеденить, глупо. один пакет выполнится более вероятно чем несколько последовательных (он же пришол на сервер и выполняется, а не пришла часть работает, следующая ждет... т.е. более растянуто во времени получается, вероятнее сбой в сети).

но дальше написанно, да есть ситуации когда надо стартануть транзакцию, после выполнить несколько действий не вкладывающихся в один скрипт, и после всего закрыть ее.
есть, но зачем использовать для работы кастрированные версии команд, если есть полные?
сравни
ADOConnection.BeginTrans
и (BOL)
SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE }
BEGIN TRAN [ SACTION ] [ transaction_name | @tran_name_variable [ WITH MARK [ 'description' ] ] ]

что стоит выполнить это в первом/последнем ADOCommand? или пусть даже выделив в отдельно выполняемые, все одно лучше чем BeginTrans.
ладно можно пользоваться не всем, но не заходя в BOL даже просто знать о возможности не будеш.

и потом, BeginTrans/... это общий метод для всех (типа) sql серверов... а если сервер/провайдер не поддерживает транзакций? что будет? а скорее всего ничего не будет, и ошибки не даст, метод то есть и вызван правильно. зато получим "логическую мину", пишем и закладываемся на целостность данных которых может и не быть.


Sergey Masloff   (2006-04-21 23:48:00) [10]

Кстати и возможность на сервере написать COMMIT поддерживается (по принципиальным соображениям!) далеко не всеми СУБД.


Sergey Masloff   (2006-04-21 23:38:00) [11]

parovoZZ ©   (16.04.06 21:40)  

 try
  ADOConnection.BeginTrans;
  ADOCmd.Execute ();

 except
  ADOConnection.RollbackTrans;

 end;
  ADOConnection.CommitTrans;

1) Как сказал Джо начало транзакции перед try
2) Коммит перед except
3) Длинные транзакции страшны не для всех СУБД ;-)))


Джо   (2006-04-21 23:18:00) [12]

> [17] sniknik ©   (21.04.06 22:50)

Пока я постиг 2 причины, по которым плохо управлять транзакциями с помощью TADOConnection: (1) они тебе не нравятся и (2) некоторые плохие программисты используют их не по назначению :).
А как же насчет [4],[5,. Пример там довольно мутно описан, но, если понадобится, могу и поподробней.

Я не придираюсь, мне на самом деле интересно, так как рел. СУБД стал заниматься недавно.


sniknik   (2006-04-21 22:50:00) [13]

> надо дополнить типа этого
этот запрос ничем дополнять не надо, нет связанных/зависяших друг от друга данных...
а вообще читай ответы...  в [3] [8] уже говорил про одиночный инсерт... или у тебя транзакции "идея фикс"? ктото сказал что это круто?

> А чем ADOConnection плох в этом плане?
попыткой "рулить" транзакциями с клиента, "приучением" делать длительные разрывы между началом и завершением транзакции... так привыкают, что начинают ими юзеровскому вводу "cansel" делать..., неполноценностью команд в нем по сравнению с командами sql сервера. (см. BOL). + не нравятся они мне (и это самое главное ;), зачем делать убого с "разрывом" если можно "спрессовать" все в один неделимый пакет? а если нет такой возможности, по логике программы и надо разделить, то зачем делать кусок проги в другом стиле (sql команда универсальна, а вот с конекта процедуру в пакет не впихнеш).


parovoZZ   (2006-04-21 22:13:00) [14]

Так, что имеем в сухом остатке...
Транзакции лучше писать в скрипте, а не пологаться на ADOConnection. Т. е. запрос

INSERT INTO Messages (Message, Author, Equipment, Date_Time, Category) VALUES (:prmMessage, :prmUser, :prmID, Now(), :prmCateg)


надо дополнить типа этого

BEGIN TRANSACTION
INSERT INTO Messages (Message, Author, Equipment, Date_Time, Category) VALUES (:prmMessage, :prmUser, :prmID, Now(), :prmCateg)
IF @@error <> 0  ROLLBACK TRANSACTION
ELSE  COMMIT TRANSACTION

А чем ADOConnection плох в этом плане?


sniknik   (2006-04-19 23:02:00) [15]

> Прямо внутри процедуры? MySQL поймёт?
а причем сдесь MySQL? в вопросе база "Любая"

пример для MSSQL "Прямо внутри процедуры"

BEGIN TRANSACTION
INSERT INTO Table2 (ParID) VALUES (1000)
INSERT INTO Table1 (ID) VALUES (1000) -- ID автоинкремент, так будет ошибка, чего и добиваемся
IF @@error <> 0
 ROLLBACK TRANSACTION -- а вот и "откат"
ELSE
 COMMIT TRANSACTION

хотя конечно пример бредовый, специально задана неправильная последовательность и запись в "незаписываемое" поле, для наглядности.  

правильнее было бы
BEGIN TRANSACTION
INSERT INTO Table1 DEFAULT VALUES
INSERT INTO Table2 (ParID) VALUES (@@Identity)
COMMIT TRANSACTION
и без всяких откатов. связь будет обеспечена т.к. выполнение в одном пакете, а единственное почему 1н запрос сработает, а второй нет (исключая конечно кривые руки которые к примеру разные типы связке сделали) это экстренный "слет" сервера, ну типа в этот момент электричество кончилось... но тогда и коммит не сработает и тразакция очистится при перерестарте.


parovoZZ   (2006-04-19 21:50:00) [16]

Прямо внутри процедуры? MySQL поймёт?


Sergey13   (2006-04-19 09:18:00) [17]

>Не понял, а как условие отката создать?
С помощью try...except


parovoZZ   (2006-04-18 19:15:00) [18]


> писать в скрипте команды ADOCmd BEGIN TRANSACTION...INSERT
> ......COMMIT TRANSACTION


Не понял, а как условие отката создать?


Sergey13   (2006-04-17 16:26:00) [19]

2 [10] parovoZZ ©   (17.04.06 16:14)
> Я имею ввиду SQL запрос вызывать из хранимой процедуры или из клиента? Или всё равно ( с точки зрения запуска транзакций)?

Абсолютно.

>Ну сами же сказали, что один инсерт оборачивать в транзакцию смысла не имеет. Ну обернём десять... Тогда что?
Лучше 100000000 сразу! 8-)
Надо "оборачивать" столько, сколько нужно - не больше и не меньше.


parovoZZ   (2006-04-17 16:14:00) [20]


> Каскадное удаление лучше делать через каскадное удаление
> по внешнему ключу. 8-)

Я имею ввиду SQL запрос вызывать из хранимой процедуры или из клиента? Или всё равно ( с точки зрения запуска транзакций)?


> > А что, ADOCommand поддерживает несколько SQL инструкций?
> ??
>столько сколько поддерживает "Любая" база...

С ADOQuery ничего не вышло. Только одна за один сеанс.


> > надо вставить пару-тройку записей по мегабайту каждая.
> они зависимы друг от друга? т.е. если не вставить последнюю
> то первая (к примеру) смысла не имеет? если имеет то не
> имеет смысла транзакция (неявная на обеспечение целосности
> самой записи и так будет). размер записи тут совсем не причем.
>

Ну сами же сказали, что один инсерт оборачивать в транзакцию смысла не имеет. Ну обернём десять... Тогда что?


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо