[Erledigt] Status eines Objekts
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
[Erledigt] Status eines Objekts
Und hier die nächste Frage:
1. Ich habe eine Klasse, deren Objekt Daten von der DB bezieht. Die Klasse hat auch ein Boolean-Property 'IsChanged'
2. Das Objekt der Klasse wird verwendet, um die Daten in der Form anzuzeigen
3. Beim Beenden der Form werden die Inhalte der Eingabefelder in das Objekt zurückgeschrieben
4. die Daten des Objekts werden in die DB eingetragen
Ich könnte nun anhand von Vergleichen (bei 3.) testen, ob sich der Wert im Objekt tatsächlich verändert hat, weil nur dann ein Speichern (4,) notwendig ist.
Dies artet aber in eine kleinere bis mittlere Orgie aus, weswegen ich mich frage, wie man das besser machen könnte.
1. Ich habe eine Klasse, deren Objekt Daten von der DB bezieht. Die Klasse hat auch ein Boolean-Property 'IsChanged'
2. Das Objekt der Klasse wird verwendet, um die Daten in der Form anzuzeigen
3. Beim Beenden der Form werden die Inhalte der Eingabefelder in das Objekt zurückgeschrieben
4. die Daten des Objekts werden in die DB eingetragen
Ich könnte nun anhand von Vergleichen (bei 3.) testen, ob sich der Wert im Objekt tatsächlich verändert hat, weil nur dann ein Speichern (4,) notwendig ist.
Dies artet aber in eine kleinere bis mittlere Orgie aus, weswegen ich mich frage, wie man das besser machen könnte.
Zuletzt geändert von MacWomble am Sa 12. Jan 2019, 12:11, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
Re: Status eines Objekts
TEdit, TCheckBox, TComboBox etc. haben alle ein OnChange Event.
Das könnte man verwenden um festzuhalten, ob sich was geändert hat.
Das könnte man verwenden um festzuhalten, ob sich was geändert hat.
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
Re: Status eines Objekts
Ja, das ist klar. Jedoch wird dies ja ständig ausgelöst wenn man am tippen ist. Ist es wirklich sinnvoll, dies hier zu verwenden?
Müsste dann jedes mal ausgeführt werden(Bsp): If Not Historie.IsChanged then Historie.IsChanged := True;
Müsste dann jedes mal ausgeführt werden(Bsp): If Not Historie.IsChanged then Historie.IsChanged := True;
Zuletzt geändert von MacWomble am Sa 12. Jan 2019, 11:24, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
Re: Status eines Objekts
Wo ist das Problem?MacWomble hat geschrieben:Ja, das ist klar. Jedoch wird dies ja ständig ausgelöst wenn man am tippen ist. Ist es wirklich sinnvoll, dies hier zu verwenden?
Müsste dann jedes mal ausgeführt werden: If Not Historie.IsChanged then Historie.IsChanged := True;
Beim jedem Tippen in ein Edit etc. passiert so viel im System, dass diese eine Boolean Abfrage nicht bemerkbar ist.
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
Re: Status eines Objekts
OK, das klingt plausibel.
Was ist dann besser?
If Not Historie.IsChanged then Historie.IsChanged := True;
oder einfach
Historie.IsChanged := True;
Was ist dann besser?
If Not Historie.IsChanged then Historie.IsChanged := True;
oder einfach
Historie.IsChanged := True;
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
Re: Status eines Objekts
Ich mache das so: das ich erst speichere, wenn ich die Komponente verlasse z.b. im onExit. Das Klappt sehr Zuverlässig.
Außerdem habe ich noch eine 3 Sekunden Regel: Wenn nach 3 Sekunden keine weiteren Daten geändert wurden wird es gespeichert.
Beim Tippen wird natürlich der Timer zurück gesetzt.
Außerdem habe ich noch eine 3 Sekunden Regel: Wenn nach 3 Sekunden keine weiteren Daten geändert wurden wird es gespeichert.
Beim Tippen wird natürlich der Timer zurück gesetzt.
MFG
Michael Springwald
Michael Springwald
Re: Status eines Objekts
"Sauber" wäre schon If not... aber der Unterschied wird minimal sein.MacWomble hat geschrieben:OK, das klingt plausibel.
Was ist dann besser?
If Not Historie.IsChanged then Historie.IsChanged := True;
oder einfach
Historie.IsChanged := True;
Insbesondere weil eine Tastatureingabe aus Sicht des Computers sowieso ein seltenes Ereignis ist.
Ich würde mir darüber keine Gedanken machen.
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
Re: Status eines Objekts
Die 3-Sekunden-Regel finde ich witzig, ist aber nicht unbedenklich:pluto hat geschrieben:Ich mache das so: das ich erst speichere, wenn ich die Komponente verlasse z.b. im onExit. Das Klappt sehr Zuverlässig.
Außerdem habe ich noch eine 3 Sekunden Regel: Wenn nach 3 Sekunden keine weiteren Daten geändert wurden wird es gespeichert.
Beim Tippen wird natürlich der Timer zurück gesetzt.
Stell dir vor, der Anwender schläft ein, schlägt mit dem Kopf auf die Tastatur und dann wird hgjsjödhgehhö gespeichert



Besser ist ein Speichern-Button und das automatische Speichern beim Verlassen der Form.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
Re: [Erledigt] Status eines Objekts
OnExit feuert aber auch, wenn nichts verändert wurde.
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1639
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: Status eines Objekts
Ich mache das normalerweise in den Setter Methoden der Properties der Klasse.
Das hat den Vorteil, dass die Property Modified wirklich nur dann true liefert, wenn eine der anderen Properties mit einem vom bisherigen Inhalt abweichenden Wert beschrieben wurde.
Gerade wenn man mit Datenbanken arbeitet, kann man sich u.U. ein Update sparen wenn z.B. der alte Name "Schmidt" ist und der Anwender in einem Edit was macht aber am Ende doch wieder "Schmidt" drinnensteht.
oft verwende ich hirarchische Strukturen die von TFPList abgeleitet sind.
In dem Fall prüft GetModified nicht nur den Status von sich selbst sondern auch den aller untergeordneter Elemente und liefert nur dann true wenn sich irgendein Element geändert hat.
Ein weiterer Vorteil besteht darin, dass in den Settermethoden (SetName, SetWhatEver usw) noch eine Plausibilitätsprüfung der neuen Inhalte gemacht werden kann.
Auf den ersten Blick sieht das nach viel Schreibarbeit aus. Es hilft einem bei größeren Projekten aber auch etwas den Überblick zu behalten. (Mein CAD Programm hat mittlerweile fast 600000 Programmzeilen und ich blicke noch immer durch), meistens jedenfalls
Gruß
Michael
Code: Alles auswählen
interface
type
TMyClassProperty = (mcpModifed, mcpVisible, ...);
TMyClassProperties = set of TMyClassProperty;
TMyClass = class
private
fFlags : TMyClassProperties; // Hier lassen sich jede Menge boolscher Flags unterbringen
fName : string;
fWhatEver : Double;
procedure SetName(const aValue : string);
procedure SetWhatEver(const aValue : Double);
procedure SetModified(const aValue : boolean);
function GetModified : boolean;
public
property Name : string read fName write SetName;
property WhatEver : integer read fWhatEver write SetWhatEver;
property Modified : boolean read GetModified write SetModified;
end;
implementation
procedure TMyClass.SetName(const aValue : string);
begin
if fName = aValue then exit; // Den neuen Wert nur dann verwenden wenn er ungleich dem bisherigen ist
fName := aValue;
Modified := True;
end;
procedure TMyClass.SetWhatEver(const aValue : integer);
begin
if fWhatEver = aValue then exit;
fWhatEver := aValue;
Modified := True;
end;
procedure TMyClass.SetModified(const aValue : boolean);
begin
if Modified = aValue then exit; // Hier muss man aufpassen, dass auf die Property Modified nicht schreibend zugegriffen wird. Sonst dreht sich es endlos im Kreis!
if aValue then
Include(fFlags, mcpModified)
else
Exclude(fFlags, mcpModified);
end;
function TMyClass.GetModified : boolean;
begin
Result := mcpModified in fFlags;
end;
Gerade wenn man mit Datenbanken arbeitet, kann man sich u.U. ein Update sparen wenn z.B. der alte Name "Schmidt" ist und der Anwender in einem Edit was macht aber am Ende doch wieder "Schmidt" drinnensteht.
oft verwende ich hirarchische Strukturen die von TFPList abgeleitet sind.
In dem Fall prüft GetModified nicht nur den Status von sich selbst sondern auch den aller untergeordneter Elemente und liefert nur dann true wenn sich irgendein Element geändert hat.
Ein weiterer Vorteil besteht darin, dass in den Settermethoden (SetName, SetWhatEver usw) noch eine Plausibilitätsprüfung der neuen Inhalte gemacht werden kann.
Auf den ersten Blick sieht das nach viel Schreibarbeit aus. Es hilft einem bei größeren Projekten aber auch etwas den Überblick zu behalten. (Mein CAD Programm hat mittlerweile fast 600000 Programmzeilen und ich blicke noch immer durch), meistens jedenfalls

Gruß
Michael
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
Re: [Erledigt] Status eines Objekts
Diese Lösung finde ich sehr gut. Alles ist in der Klasse geregelt, unabhängig von der Eingabemaske.
Das spart die vielen OnChange-Ereignisse und macht das ganze dadurch auch noch sicherer (Vergessenes OnChange = kein Speichern).
Das spart die vielen OnChange-Ereignisse und macht das ganze dadurch auch noch sicherer (Vergessenes OnChange = kein Speichern).
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
Re: [Erledigt] Status eines Objekts
Das ist richtig, dort kann man aber prüfen, ob was verändert wurde oder nicht.... wo ist das Problem?OnExit feuert aber auch, wenn nichts verändert wurde.
Das macht im Prinzip nichts, da es eine Histroytable gibt.Stell dir vor, der Anwender schläft ein, schlägt mit dem Kopf auf die Tastatur und dann wird hgjsjödhgehhö gespeichert
dank dieser tabelle kann man jede Änderung wider rückgängig machen.
Zum Beispiel bei Google-Docs wird ebenfalls Automatisch gespeichert ob nach 3 Sekunden weiß ich nicht, aber so bin ich auf die Idee gekommen.
edit: Ich finde es viel schlimmer daten zu verlieren, weil ich kurz weg gehe und dann der Strom ausfällt oder irgendwas anders nicht ok ist z.b. so ein Windws Update meint, den PC neu zu starten(kann unter linux auch mal vorkommen....)
Altmodisch....(Aus meiner Sicht).Besser ist ein Speichern-Button und das automatische Speichern beim Verlassen der Form.
Zur Zeit mache ich es ebenfalls so... Weil sich nur das Übernommen werden muss was auch geändert wurde.Ich mache das normalerweise in den Setter Methoden der Properties der Klasse.
Z.B. wenn ich ein From habe mit 12 Eingabefeldern, sich aber nur 1 Feld Geändert hat, warum gleich alles speichern?
MFG
Michael Springwald
Michael Springwald
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
Re: [Erledigt] Status eines Objekts
Einen Speichern-Button benötige ich auch nicht, da die Form zum Editieren / Eingeben eines einzelnen Datensatzes aufgerufen und nach der Eingabe direkt wieder geschlossen wird. Über eine automatische (zeitliche) Sicherung nachzudenken ist sicher sinnvoll. Aber ich konnte mir den Wink nicht verkneifen.
Ich schreibe bzw aktualisiere den kompletten Datensatz, wenn irgend ein Feld geändert wurde.
Ich verfolge im Moment zwei Ziele in meinem Projekt:
1. Die Datenmaske soll die Datenbank (oder was auch immer) nicht kennen
2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.
Ersteres realisiere ich durch die Einführung von Datenklassen, letzteres durch den Tipp von Fliegermichl
Ich habe hierfür ein kleines Testprojekt und alles funktioniert nun wie gewünscht - Bisher, aber ich habe noch einiges vor mir

Kam bei mir unter Linux noch nie vor, kann ich mir auch nicht vorstellen - aber von Windoof kenne ich das nochso ein Windws Update meint, den PC neu zu starten(kann unter linux auch mal vorkommen....)

... und wie viele Schreibzugriffe hast du, wenn 12 Felder geändert werden, bzw wie gehst du dann vor?Z.B. wenn ich ein From habe mit 12 Eingabefeldern, sich aber nur 1 Feld Geändert hat, warum gleich alles speichern?
Ich schreibe bzw aktualisiere den kompletten Datensatz, wenn irgend ein Feld geändert wurde.
Ich verfolge im Moment zwei Ziele in meinem Projekt:
1. Die Datenmaske soll die Datenbank (oder was auch immer) nicht kennen
2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.
Ersteres realisiere ich durch die Einführung von Datenklassen, letzteres durch den Tipp von Fliegermichl
Ich habe hierfür ein kleines Testprojekt und alles funktioniert nun wie gewünscht - Bisher, aber ich habe noch einiges vor mir

Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
-
- Lazarusforum e. V.
- Beiträge: 7192
- Registriert: So 19. Nov 2006, 12:06
- OS, Lazarus, FPC: Linux Mint 19.3
- CPU-Target: AMD
- Wohnort: Oldenburg(Oldenburg)
Re: [Erledigt] Status eines Objekts
Meine DB Funktion ist extra dafür ausgelegt. es gibt eine Klasse z.b. für alle 12 Felder und wenn sich dort was ändert wird eine Variable auf true gestellt....... und wie viele Schreibzugriffe hast du, wenn 12 Felder geändert werden, bzw wie gehst du dann vor?
Daraus mache ich dann eine Datenbank abfrage.... Die Funktion ist recht lang und noch nicht aufgeräumt.....
Code: Alles auswählen
procedure TPLNoteManager2.EditNote(const aNote: TPLNoteManagerNoteItem);
var
str:string;
UsedNoteID, UsedDirectoryID, UsedTitle, UsedText:Boolean;
UsedCreateDateTime, UsedLastReadDateTime, UsedLastWriteDatetime:Boolean;
UsedReadCount, UsedWriteCount, UsedEncryptByUserPassword:Boolean;
FirstFeld:Boolean;
begin
str := 'UPDATE NoteTable SET ';
UsedNoteID:=false; UsedDirectoryID:=false; UsedTitle:=false; UsedText:=false;
UsedCreateDateTime:=false; UsedLastReadDateTime:=false; UsedLastWriteDatetime:=false;
UsedReadCount:=false; UsedWriteCount:=false; UsedEncryptByUserPassword:=false;
FirstFeld:=False;
{ if aNote.NoteID > 0 then begin
FirstFeld:=true;
str:=str+'NoteID=:NoteID';
UsedNoteID:=true;
end; // NoteID}
if aNote.DirectoryID > 0 then begin
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'DirectoryID=:DirectoryID';
UsedDirectoryID:=true;
end; // DirectoryID
if aNote.Title <> '' then begin
writeln('Title');
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'Title=:Title';
UsedTitle:=true;
end; // Title
if aNote.Text <> '' then begin
writeln('Text');
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'Text=:Text';
UsedText:=true;
end; // Text
if aNote.CreateDateTime > 0 then begin
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'CreateDateTime=:CreateDateTime';
UsedCreateDateTime:=true;
end; // CreateDateTime
if aNote.LastReadDateTime > 0 then begin
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'LastReadDateTime=:LastReadDateTime';
UsedLastReadDateTime:=true;
end; // LastReadDateTime
if aNote.LastWriteDateTime > 0 then begin
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'LastwriteDateTime=:LastwriteDateTime';
UsedLastWriteDatetime:=true;
end; // LastWriteDateTime
if aNote.ReadCount > 0 then begin
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'ReadCount=:ReadCount';
UsedReadCount:=true;
end; // ReadCount
if aNote.WriteCount > 0 then begin
if FirstFeld then
str:=str+','
else
FirstFeld:=true;
str:=str+'WriteCount=:WriteCount';
UsedWriteCount:=true;
end; // WriteCount
str:=str+' WHERE NoteID = ' + IntToStr(aNote.NoteID)+';';
writeln(#13,str);
MariaDB.Query.SQL.Text:=str;
if UsedNoteID then MariaDB.Query.ParamByName('NoteID').AsInteger:=aNote.NoteID;
if UsedDirectoryID then MariaDB.Query.ParamByName('DirectoryID').AsInteger:=aNote.DirectoryID;
if UsedTitle then MariaDB.Query.ParamByName('Title').AsString:=aNote.Title;
if UsedText then MariaDB.Query.ParamByName('Text').AsString:=aNote.Text;
if UsedCreateDateTime then MariaDB.Query.ParamByName('CreateDateTime').AsDateTime:=aNote.CreateDateTime;
if UsedLastReadDateTime then MariaDB.Query.ParamByName('LastReadDateTime').AsDateTime:=aNote.LastReadDateTime;
if UsedLastWriteDatetime then MariaDB.Query.ParamByName('LastwriteDateTime').AsDateTime:=aNote.LastWriteDateTime;
if UsedReadCount then MariaDB.Query.ParamByName('ReadCount').AsInteger:=aNote.ReadCount;
if UsedWriteCount then MariaDB.Query.ParamByName('WriteCount').AsInteger:=aNote.WriteCount;
MariaDB.Query.ExecSQL;
MariaDB.ATransaction.Commit;
writeln('TPLNoteManager2.EditNote');
end; // TPLNoteManager2.EditNote
Diese Procedure funktioniert sehr gut.
Der Code kommt aus meinem Aktuellen Prototyp.
Es mag Fälle geben, da geht es nicht anders... mein Ansatz war es Datentraffic zu sparen. Die Genannte Procedur haben, ist teil einer Rest Api die ich gerade schreibe.Ich schreibe bzw aktualisiere den kompletten Datensatz, wenn irgend ein Feld geändert wurde.
Richtig, ist bei mir auch der Fall.... Hinzu kommt, das zwischen Rest-server und Rest-Client ein Jsonen-Austausch Protokoll verwendet wird...1. Die Datenmaske soll die Datenbank (oder was auch immer) nicht kennen
Eben... darum nur das speichern, was sich auch geändert hat.2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.
MFG
Michael Springwald
Michael Springwald
-
- Lazarusforum e. V.
- Beiträge: 999
- Registriert: Do 17. Apr 2008, 01:59
- OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
- CPU-Target: Intel i7-10750 64Bit
- Wohnort: Freiburg
Re: [Erledigt] Status eines Objekts
Gut, aber in der Regel sind die Datensätze nicht groß und meines Erachtens ist es unerheblich ob ich nun den kompletten Satz aktualisiere oder nur die geänderten Felder.pluto hat geschrieben:.Eben... darum nur das speichern, was sich auch geändert hat.2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.
In jedem Fall habe ich ein Update zu schreiben. Bei Insert ist es unter Umständen sogar kritisch, zumindest wenn diese keinen Default in der DB definiert haben.
Natürlich hast du Recht, wenn du vom Trafficvolumen ausgehst.
Du kennst ja mein CTR-BOSS vom sehen und ich kann dir sagen, dass es im Moment unzählige überflüssige Lese- und Schreibzugriffe macht, was den festen Datenbindungen an die Formcontrols zu schulden ist.
Mit den Klassen bin ich da nun wirklich auf das notwendige Minimum unten - weniger geht tatsächlich nur wenn man es so minimiert, wie du es tust.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.