🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: pl.comp.lang.delphi.bazy-danych
7 messages
7 total messages Started by ufo Wed, 09 Dec 2015 23:36
Logika pewnej transakcji...
#80958
Author: ufo
Date: Wed, 09 Dec 2015 23:36
76 lines
2872 bytes
Witam, mam problem z cofaniem transakcji w przypadku błędu w
następującej sytuacji:

- użytkownik używa pewnego formularza do edycji lub dodawania danych
(wybiera funkcję Insert lub Edit), jeżeli wybrał Edit, to edytowany
rekord jest blokowany (lmPessimistic) i rozpoczyna się transakcja
(implikowana przez komponenty UNIDAC), w przypadku Insert nic nie jest
blokowane

- użytkownik wprowadził dane i naciska OK by je zapisać, wykonuje się
kod typu (w dużym uproszczeniu):

db.StartTransaction;
try
   Table1.Post;
   Table2.Post;
   ....
   db.Commit;
except
   raise; //błąd - powrót do formularza i ewentualne poprawki
end;
...

- Jak widać zapisuje się wiele tabel i jeśli wystąpił błąd to raise
wraca nas do formularza, gdzie użytkownik może poprawić ew. błędy ( o
ile wynikły z błędów wprowadzania).
Niestety działa to dobrze tylko w trybie Edit (użytkownik może powtarzać
tą procedurę aż do pomyślnego zapisu), w trybie Insert MUSZĘ zakończyć
transakcję, ponieważ w tym trybie blokuje ona całą tabelę (w Edit tylko
jeden wiersz) dlatego powyższy kod ulega zmianie na:

db.StartTransaction;
try
   Table1.Post;
   Table2.Post;
   ....
   db.Commit;
except
   if (EditMode=dsInsert) then //EditMode zmienna określająca tryb edycji
   begin
     mdata.db.Commit; //By nie blokwac całej tabeli
     InsertId:=table1.FieldByName(PrimaryKey).AsInteger;
     if (InsertId>0) then //zapisał sie głowny rekord
     begin
       mdata.db.StartTransaction; //nowa transakcja
       table1.Edit; //blokada rekordu
     end;
   end;
end;
...

1. Jak widać z powyższego kodu, gdy nie uda się zapisać pierwszej tabeli
(InsertId=0) to wracamy do formularza nic nie robiąc bo table1 jest
nadal w trybie dsInsert.
2. Gdy pierwsza i ew. następne tabele się zapisały (InsertId>0) to
przełączamy się tryb Edit (table.1Edit), blokując tylko jeden rekord a
nie całą tablę i operację można znowu powtarzać aż do skutecznego zapisu.

ALE nie podoba mi się to do końca bo:
1. Inni użytkownicy widzą rekord wpisany przy Insert (nie mogą go
edytować, bo jest zablokowany) - to da się przeboleć
2. Jeśli użytkownik znudził się próbami zapisu i nacisnął Cancel, to
rekordy w tabelach, które udało się zapisać będzie trzeba usunąć (to da
się przeboleć), ale może to się też nie udać bo np. zerwało połączenie i
wtedy niekompletny zestaw danych będzie gdzieś tam wisiał na zawsze...
(to już może być bolesne), co prawda można go jakoś oznaczyć i po
pomyślnym zapisie wszystkich table usunąć ta flagę.

Czy znacie jakieś bardziej "eleganckie" sposoby przeprowadzenia takiej
transakcji?. Przychodzi mi jeszcze do głowy Rollback i table1.Insert ale
wtedy użytkownik traci dane w formularzu i trzeba by je jakoś przywracać.

Z góry dziękuję
Michał


Re: Logika pewnej transakcji...
#80959
Author: zpksoft
Date: Thu, 10 Dec 2015 00:50
12 lines
338 bytes
>ciach> Czy znacie jakie¶ bardziej "eleganckie" sposoby przeprowadzenia takiej 
> transakcji?. Przychodzi mi jeszcze do g³owy Rollback i table1.Insert ale 
> wtedy u¿ytkownik traci dane w formularzu i trzeba by je jako¶ przywracaæ.
> 
> Z góry dziêkujê
> Micha³

sql update zamiast edit w dbgridzie.

Pawe³
Re: Logika pewnej transakcji...
#80960
Author: szemrany
Date: Thu, 10 Dec 2015 11:51
10 lines
296 bytes
On Thu, 10 Dec 2015 00:50:30 -0800 (PST), zpksoft wrote:

> sql update zamiast edit w dbgridzie.

A biblioteka dostępowa jak wg Ciebie robi zmiany w bazie?

--
howgh
szemrany
"Trzeba z żywymi naprzód iść, po życie sięgać nowe,
a nie w uwiędłych laurów liść z uporem stroić głowę"
Re: Logika pewnej transakcji...
#80961
Author: ufo
Date: Thu, 10 Dec 2015 18:03
16 lines
773 bytes
W dniu 2015-12-10 o 11:51, szemrany pisze:
> On Thu, 10 Dec 2015 00:50:30 -0800 (PST), zpksoft wrote:
>
>> sql update zamiast edit w dbgridzie.
>
> A biblioteka dostępowa jak wg Ciebie robi zmiany w bazie?
>
Tam w ogóle nie ma grida. Zapisywany obiekt to opis magazynu, kolejne
tabele to Magazyn (table1) (zwiera unikalne klucze jak kod i nazwa -
tylko tu może powstać błąd zależny od danych wpisanych przez
użytkownika), Adres magazynu (table2) i Stany magazynowe (table3) oraz
kilka pomniejszych tabel. Ponieważ istnienie każdej z nich bez innej
jest bez sensu, to zapisuję to w transakcji.

W każdym razie zrobiłem to lepiej, korzystając z CachedUpdates, teraz
już da się bez problemu wrócić do formularza i edytować i zapisywać aż
do skutku.
Re: Logika pewnej transakcji...
#80962
Author: zpksoft
Date: Thu, 10 Dec 2015 23:57
15 lines
377 bytes
W dniu czwartek, 10 grudnia 2015 11:51:54 UTC+1 u¿ytkownik szemrany napisa³:
> On Thu, 10 Dec 2015 00:50:30 -0800 (PST), zpksoft wrote:
> 
> > sql update zamiast edit w dbgridzie.
> 
> A biblioteka dostêpowa jak wg Ciebie robi zmiany w bazie?
> 
> -- 
> howgh
> szemrany

Nie wiem. Pewnie blokuje rekord na czas edycji. Ale mo¿e to byæ za ma³o.

Pawe³
Re: Logika pewnej transakcji...
#80963
Author: wloochacz
Date: Fri, 11 Dec 2015 10:50
14 lines
680 bytes
W dniu 2015-12-09 o 23:36, ufo pisze:
> Czy znacie jakieś bardziej "eleganckie" sposoby przeprowadzenia takiej
> transakcji?. Przychodzi mi jeszcze do głowy Rollback i table1.Insert ale
> wtedy użytkownik traci dane w formularzu i trzeba by je jakoś przywracać.
Znacie; CachedUpdates, dopóki user nie naciśnie zapisz, nic nie idzie do
bazy danych.
Przy Insert masz to co chciałeś, ale przy Edit musisz "jakoś" zablokować
dokument przed edycją.
Pewnie UNIDAC coś tam potrafi zrobić, ale ja używam własnej implmentacji
z blokowanie całkowicie zarządzanym przez apliakację (wiem kto, kiedy i
z jakiej maszyny zablokował jaki obiekt biznesowy).


--
wloochacz
Re: Logika pewnej transakcji...
#80964
Author: ufo
Date: Fri, 11 Dec 2015 18:14
70 lines
3074 bytes
W dniu 2015-12-11 o 10:50, wloochacz pisze:
> W dniu 2015-12-09 o 23:36, ufo pisze:
>> Czy znacie jakieś bardziej "eleganckie" sposoby przeprowadzenia takiej
>> transakcji?. Przychodzi mi jeszcze do głowy Rollback i table1.Insert ale
>> wtedy użytkownik traci dane w formularzu i trzeba by je jakoś przywracać.
> Znacie; CachedUpdates, dopóki user nie naciśnie zapisz, nic nie idzie do
> bazy danych.
> Przy Insert masz to co chciałeś, ale przy Edit musisz "jakoś" zablokować
> dokument przed edycją.
> Pewnie UNIDAC coś tam potrafi zrobić, ale ja używam własnej implmentacji
> z blokowanie całkowicie zarządzanym przez apliakację (wiem kto, kiedy i
> z jakiej maszyny zablokował jaki obiekt biznesowy).
>
>
No, zapis informacji o blokadzie może być przydatny i tez to później
wprowadzę. Oczywiście można korzystać z blokowania przez db lub z
własnego mechanizmu. Można także rozważyć lokalną kopię, gdyby komputer
użytkownika wywalił się w połowie zapisu dużego dokumentu.
Unidac z CachedUpdates blokuje rekord w trybie edycji i ustawionym
trybie blokady Pessimistic. Jeśli anuluje się transakcję wystarczy
uruchomić nową i zablokować ponownie funkcją Lock.
Blokowanie przy użyciu mechanizmów db jest wygodne, może mieć jednak
uboczne skutki, bo niektóre engine, zamiast pojedynczego rekordu,
blokują całą stronę (kilka rekordów).

W każdym razie zamieściłem ostateczną wersję na CachedUpdates. Tu każda
tabela po pomyślnym zapisie, dodaje się do listy. A w razie błędu jest
przywracana do stanu przed zapisem (RestoreUpdates) a całośc zapisu
anulowana przez Rollback.
Troszkę tu moich "overridowanych" funkcji ale powinno być zrozumiałe.

    if (not mdata.db.InTransaction) then mdata.db.StartTransaction;
     Tables:=TList.Create;
     try
       try
         Tables.Capacity:;
         ErrTable:ÚtaQuery;
         if (DataQuery.State in [dsEdit, dsInsert]) then DataQuery.Post;
         if (DataQuery.UpdatesPending) then DataQuery.ApplyUpdates;
         Tables.Add(DataQuery);
         ErrTable:­resFRM.adresQUE;
         adresFRM.Post(EditMode,15,PrimaryKey,'usr_Key','',false); //to
robi post+applyupdates dla tej tabeli
         Tables.Add(adresFRM.adresQUE);
         ErrTable:=telfaxFRM.DataQUE;
         telfaxFRM.ApplyUpdates;
         Tables.Add(telfaxFRM.DataQUE);
         ErrTable:=emailFRM.DataQUE;
         emailFRM.ApplyUpdates;
         Tables.Add(emailFRM.DataQUE);
         ErrTable:=komunikatorFRM.DataQUE;
         komunikatorFRM.ApplyUpdates;
         Tables.Add(komunikatorFRM.DataQUE);
       except on E:Exception do
         begin
           mdata.db.Rollback;
           for i := 0 to Tables.Count-1 do
TUniQuery(Tables[i]).RestoreUpdates;
           if EditMode=dsEdit then
           begin
             mdata.db.StartTransaction;
             DataQuery.Lock;
           end;
           if (E is EUniError) then mdata.DataBaseError(E as
EUniError,Self,ErrTable);
           raise;
         end;
       end;
     finally
       Tables.Free;
     end;
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads