🚀 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 22:36
Logika pewnej transakcji...
#82634
Author: ufo
Date: Wed, 09 Dec 2015 22:36
74 lines
3758 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...
#82635
Author: zpksoft
Date: Thu, 10 Dec 2015 08:50
10 lines
1501 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...
#82636
Author: szemrany
Date: Thu, 10 Dec 2015 10:51
11 lines
1435 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...
#82637
Author: ufo
Date: Thu, 10 Dec 2015 17:03
17 lines
1764 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...
#82638
Author: zpksoft
Date: Fri, 11 Dec 2015 07:57
14 lines
1742 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...
#82639
Author: wloochacz
Date: Fri, 11 Dec 2015 09:50
15 lines
1653 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...
#82640
Author: ufo
Date: Fri, 11 Dec 2015 17:14
71 lines
4068 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:=10;
         ErrTable:=DataQuery;
         if (DataQuery.State in [dsEdit, dsInsert]) then DataQuery.Post;
         if (DataQuery.UpdatesPending) then DataQuery.ApplyUpdates;
         Tables.Add(DataQuery);
         ErrTable:=adresFRM.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