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...
Author: ufo
Date: Wed, 09 Dec 2015 22:36
Date: Wed, 09 Dec 2015 22:36
74 lines
3758 bytes
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...
Author: zpksoft
Date: Thu, 10 Dec 2015 08:50
Date: Thu, 10 Dec 2015 08:50
10 lines
1501 bytes
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...
Author: szemrany
Date: Thu, 10 Dec 2015 10:51
Date: Thu, 10 Dec 2015 10:51
11 lines
1435 bytes
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...
Author: ufo
Date: Thu, 10 Dec 2015 17:03
Date: Thu, 10 Dec 2015 17:03
17 lines
1764 bytes
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...
Author: zpksoft
Date: Fri, 11 Dec 2015 07:57
Date: Fri, 11 Dec 2015 07:57
14 lines
1742 bytes
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...
Author: wloochacz
Date: Fri, 11 Dec 2015 09:50
Date: Fri, 11 Dec 2015 09:50
15 lines
1653 bytes
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...
Author: ufo
Date: Fri, 11 Dec 2015 17:14
Date: Fri, 11 Dec 2015 17:14
71 lines
4068 bytes
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