🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.lang.delphi.bazy-danych
Article #82045

Re: Transakcje MSSQL2008, ANYDAC

#82045
From: wloochacz
Date: Thu, 24 Apr 2014 19:44
161 lines
7159 bytes
W dniu 2014-04-18 22:32, feniks pisze:
> Witam
>
> Zasadniczo moim celem jest zabezpieczenie operacji i przy wystąpieniu jakiegokolwiek problemu wycofanie całej operacji i poinformowanie użytkownika, iż coś jest nie tak.
OK, bo mnie lesioq wk (widzisz jaki ze mnie prosty chłop, tak łatwo daję 
sie podejść) to Ci napiszę co i jak ;-)

> Życie pokazało, iż to mi się nie udało dlatego zwracam się z uprzejma prośbą o pomoc, pokazanie jak taki kod powinien wyglądać
> Umieszczony kod jest częścią kodu właściwego z dużą ilością warunków i aby pokazać to co dla mnie ważne w tym poście część linijek wyciąłem i trochę za dużo gdyż jak słusznie Pan zauważył brakuje DM.up.sql.Clear.
Pytanie #1:
Czy CachedUpdates dla jest w ogóle włączone?
czyli DM.dane.Cachedudpates := True;  ??
Zakładam, że tak jest...

Pytanie #2:
Wiesz czym jest i jak działa CachedUpdates?
Jesli nie wiesz, to spójrz:
http://docwiki.embarcadero.com/Libraries/XE6/en/FireDAC.Comp.DataSet.TFDDataSet.CachedUpdates

Na początek wystarczy to zdanie: "CachedUpdates enables (True) or 
disables (False) the logging of data changes (Insert/Post, Edit/Post, 
Delete) without immediately applying them to the database."

A więc wszelkie zmiany w DataSecie (DM.dane), które robisz za pomocą 
Insert/Append, Edit, Delete nie "wchodzą" do bazy danych. Dane są 
zmieniane (i logowane - jest też mechanizm Undo/Redo oraz SavePoints - 
lesioq wyjaśni Ci co są te SavePointy w kontekście Nested Transaction, 
bo on to wrzuca do T-SQL'a) tylko i wyłącznie w lokalnym buforze DataSetu.
Żeby zmiany z lokalnego bufora poleciały do bazy danych, należy wywołać:
ApplyUpdates - do zatwierdzenia zmian.
CommitUpdates - kasuje log zmian dla rekordów z lokalnego bufora 
DataSetu i oznacza wszystkie rekordy jako niezmodyfikowane (ciekaowstka 
- w AnyDAC/FireDAC jest mechnizm typu row-level-versioning, ale to inna 
bajka...)
CancelUpdates - do anulowania zmian.

OK, tyle wstępu...

Pierwszy problem, to wywolanie ApplyUpdates - napisałeś tak:

DM.dane.ApplyUpdates;

Ale ApplyUpdates zadeklarowano tak:
function ApplyUpdates(AMaxErrors: Integer = -1): Integer;

AMaxErrors odpowiada ile błędów jest dopuszczone podczas aktualizacji 
danych w bazie. -1 oznacza - bez limitu, co sprawia, że ApplyUpdates 
aktulizuje dane w bazie na podstawie logu zmian w DataSecie - wiersz po 
wierszu. Jeśli w momencie aktualizacji (czyli kiedy wołasz ApplyUpdates) 
wiersza wystąpi wyjątek (z różnych powodów, np. naruszenie klucza, 
wartośc null dla pola not-null, albo zaniku połączenia z serwerem), to 
dzieje się tak:
1) Jeżeli AMaxErrors < 0, to nie przejmuj się i przejdź do aktulizacji 
następnego wiersza.
2) Jeśli był bład, to wołaj zdarzenie OnReconcileError


Zatem, Twój kod powinien być napisany tak (ale to wersja biedna, bo nie 
powinno być częściowych aktualkizacji w bazie ale nadal nie będziesz 
widział co poszło nie tak):

var
   lErrorCount: Integer;
begin
   DM.ADConnection1.StartTransaction;
   try
     if DM.dane.State in dsEditModes then
       DM.dane.Post;

     if DM.dane.UpdatesPending then
     begin
       lErrorCount := DM.dane.ApplyUpdates(0);
       if lErrorCount = 0 then
         DM.dane.CommitUpdates;
     end
     else
       lErrorCount := 0;

     if lErrorCount = 0 then
     begin
       DM.up.sql.Text := 'UPDATE prac_war SET s_wn = s_wn+:zkw  WHERE 
prac_war.SYMBOL_P = :zsymbol_p';
       DM.up.ParamByName('zkw').Value       := zkwota_s;
       DM.up.ParamByName('zsymbol_p').Value := zsymbol_p;
       DM.up.ExecSQL;
       DM.ADConnection1.Commit;
     end
     else
       DM.ADConnection1.Rollback;
   except
     DM.ADConnection1.Rollback;
     MessageBox(Handle, 'Operacja nie powiodła się !!!!', 'Błąd', mb_OK 
+ MB_ICONSTOP);
   end;
end;

Jeśli chcesz dowiedzieć się co poszło ni tak, masz kilka opcji:
1) Obsłużyć zdarzenie OnUpdateErrror
2) Obsłużyć zdarzenie OnReconcileError
3) Użyć FilterChanges

Pokażę z tą trzecią opcją, czyli:
var
   lErrorCount: Integer;
   lST        : TStringList;
begin
   DM.ADConnection1.StartTransaction;
   try
     if DM.dane.State in dsEditModes then
       DM.dane.Post;

     if DM.dane.UpdatesPending then
     begin
       lErrorCount := DM.dane.ApplyUpdates(-1);
       if lErrorCount = 0 then
         DM.dane.CommitUpdates;
     end
     else
       lErrorCount := 0;

     if lErrorCount = 0 then
       begin
         DM.up.sql.Text := 'UPDATE prac_war SET s_wn = s_wn+:zkw  WHERE 
prac_war.SYMBOL_P = :zsymbol_p';
         DM.up.ParamByName('zkw').Value       := zkwota_s;
         DM.up.ParamByName('zsymbol_p').Value := zsymbol_p;
         DM.up.ExecSQL;
         DM.ADConnection1.Commit;
       end
     else
       begin
         DM.dane.FilterChanges := [rtModified, rtInserted, rtDeleted, 
rtHasErrors];
         lST := TStringList.Create;
         try
           DM.dane.First;
           while not DM.dane.Eof do
             if Assigned(DM.dane.RowError) then
               lST.Add(DM.dane.RowError.Message);

           if lST.Count > 0 then
             MessageBox(Handle, 'Operacja nie powiodła się !!!!'#13#10 + 
lST.Text, 'Błąd', mb_OK + MB_ICONSTOP);
         finally
           lST.Free;
           DM.dane.FilterChanges := [rtUnmodified, rtModified, rtInserted];
           DM.ADConnection1.Rollback;
         end;
       end;
   except
     DM.ADConnection1.Rollback;
     MessageBox(Handle, 'Operacja nie powiodła się !!!!', 'Błąd', mb_OK 
+ MB_ICONSTOP);
   end;
end;

PS.
Nie sprawdzałem kodu, może nie działać od ręki...

-- 
wloochacz
Technical Details
Message-ID: <535969af$0$2246$65785112@news.neostrada.pl>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!agh.edu.pl!news.agh.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!news.nask.pl!news.nask.org.pl!news.unit0.net!usenet.blueworldhosting.com!feeder01.blueworldhosting.com!peer01.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!nx01.iad01.newshosting.com!newshosting.com!newsfeed.neostrada.pl!unt-exc-01.news.neostrada.pl!unt-spo-a-01.news.neostrada.pl!news.neostrada.pl.POSTED!not-for-mail
References: <c776d48b-fec7-421f-89f2-fbed5ec8f316@googlegroups.com> <5350f511$0$2246$65785112@news.neostrada.pl> <1383d8eb-5c02-40aa-bc0e-60f9c1b9dd7b@googlegroups.com>

Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!agh.edu.pl!news.agh.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!news.nask.pl!news.nask.org.pl!news.unit0.net!usenet.blueworldhosting.com!feeder01.blueworldhosting.com!peer01.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!nx01.iad01.newshosting.com!newshosting.com!newsfeed.neostrada.pl!unt-exc-01.news.neostrada.pl!unt-spo-a-01.news.neostrada.pl!news.neostrada.pl.POSTED!not-for-mail
Date: Thu, 24 Apr 2014 21:44:22 +0200
From: wloochacz <wloochacz@no.spam.gmail.com>
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Thunderbird/24.4.0
MIME-Version: 1.0
Newsgroups: pl.comp.lang.delphi.bazy-danych
Subject: Re: Transakcje MSSQL2008, ANYDAC
References: <c776d48b-fec7-421f-89f2-fbed5ec8f316@googlegroups.com> <5350f511$0$2246$65785112@news.neostrada.pl> <1383d8eb-5c02-40aa-bc0e-60f9c1b9dd7b@googlegroups.com>
In-Reply-To: <1383d8eb-5c02-40aa-bc0e-60f9c1b9dd7b@googlegroups.com>
Lines: 161
Message-ID: <535969af$0$2246$65785112@news.neostrada.pl>
Organization: Telekomunikacja Polska
NNTP-Posting-Host: 83.25.255.13
X-Trace: 1398368688 unt-rea-b-01.news.neostrada.pl 2246 83.25.255.13:14854
X-Complaints-To: abuse@news.neostrada.pl
X-Received-Bytes: 6845
X-Received-Body-CRC: 3363227285
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit