Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Object Daten-Layer (https://www.delphipraxis.net/158336-object-daten-layer.html)

hanspeter 14. Feb 2011 09:33

Datenbank: Firebird • Version: 2.5 • Zugriff über: IBDAC

Object Daten-Layer
 
Hallo,
für ein größeres Projekt halte ich die Daten in einem Object und bräuchte hier mal einen Denkanstoß.
Beispiel eine Kundenadresse.
Delphi-Quellcode:
TKunde=class
private
  fid : integer;
  fplz : string;
  fort : string;
public
  procedure Load(ID: Integer);
  procedure Save;
  property id : integer read fid write fid;
  property plz: string read fplz write fplz;
  property ort: string read fort write fort;
end;
Load läd eine Adresse aus der Datenbank.
Save soll nur Änderungen speichern.
Also z.B.
Delphi-Quellcode:
Kunde.Load(1234);
Kunde.Plz := 1234;
Kunde.Save
;

Mir geht es jetzt darum, wie löse ich am elegantesten (und schnellsten) das Problem festzustellen ob und welche Felder geändert wurden.
Mir fallen hier zwei Methoden ein.

1. Setter für jeden Property.
Delphi-Quellcode:
procedure TKunde.SetPlz(Val: String);
begin
  if Val <> fPLZ then plzChange := true;
  fPLZ := Val;
end;
Da hat den Nachteil, das ich für jede property einen eigenen Setter schreiben muss.

2. Orginalwerte aus der Datenbank speichern und jedes Feld doppelt halten.
In der Procedure Save dann die Feldinhalte vergleichen.

if fPLZ <> OrgPLZ then ...

Eine weitere Überlegung ist das Füllen dieses Objektes mit Daten aus der Datenbanktabelle.
Ist nachfolgender Weg praktikabel?

Alle lokalen Felder haben den gleichen Namen wie die Datenbankfelder und bekommen einen Präfix (z.B. "f")
Mit "Select * from Tabelle WHERE ID=:ID wird der gewünschte Satz selektiert. Die Wertzuordnung erfolgt über RTTI zur Laufzeit.

Hat wer noch einen anderen Vorschlag?

Ich möchte mir einen kleinen Geerator schreiben, der die lokale Feldliste und die Propertys aus der Select-Anweisung generiert.
Die Klasse erledigt noch andere Aufgaben.
Funktionsfähig ist bereits das Füllen und Auslesen der Daten in einer Bildschirmmaske.

Für ein paar Ideen oder Denkanstöße wäre ich dankbar.

Gruß
Peter

Lemmy 14. Feb 2011 10:07

AW: Object Daten-Layer
 
hi,

was mir ganz spontan einfällt: Mach ein Stringlist in der Du KEy=Value die Änderungen protokollierst, d.h. im Setter des property musst Du die Property und den neuen Wert in die Stringlist speichern. Beim Save gehst Du dann die Stringlist durch und speicherst die entsprechende Werte in der DB.

Wenn Du alle Änderungen in der SL speicherst könntest Du darüber auch eine Undo-Funktion bauen.


Füllen des Objekets: Klar über RTTI. den Präfix würde ich persönlich weglassen (was soll das F?) oder die ungarische Notation verwenden.
Problem dabei ist aber, dass in Datenbanken die Länge der Feldnamen beschränkt sein könnte (z.B. in Firebird) - in diesem Fall also kurze Feldnamen verwenden oder über eine Zuordnungsliste gehen (FeldnameDB=PropertyName) oder über die Attribute (wenn die neue RTTI verwendet wird).

Grüße

Sir Rufo 14. Feb 2011 10:22

AW: Object Daten-Layer
 
wie wäre es damit
Beim Lesen aus der DB müssen natürlich beide arrays (FOldData, FNewData) mit den Werten aus der DB befüllt werden.
Beim Speichern kann man nun diese Daten vergleichen.

Hieraus lässt sich auch schön eine Basisklasse bilden und die abgeleitete hat eigentlich nur noch die properties und benutzt die Getter/Setter der Basisklasse
Delphi-Quellcode:
type
  TKunde = class
  private
    FOldData = array of variant;
    FNewData = array of variant;
    procedure InternalSetter( index : integer; Value : Variant );
    function InternalGetter( index : integer ) : Variant;
  protected
    procedure SetIntData( index : integer; const Value : integer );
    function GetIntData( index : integer ) : integer;
    procedure SetStrData( index : integer; const Value : string );
    function GetStrData( index : integer ) : string;
  public
  published
    property id : integer index 0 read GetIntData write SetIntData;
    property plz : string index 1 read GetStrData write SetStrData;
    property ort : string index 2 read GetStrData write SetStrData;
  end;

procedure TKunde.InternalSetter( index : integer; Value : Variant );
begin
  FNewData[ index ] := Value;
end;

function InternalGetter( index : integer ) : Variant;
begin
  Result := FNewData[ index ];
end;

procedure TKunde.SetIntData( index : integer; const Value : integer );
begin
  InternalSetter( index, Value );
end;

function TKunde.GetIntData( index : integer ) : integer;
begin
  Result := VarToInt( InternalGetter( index ) );
end;

DeddyH 14. Feb 2011 10:30

AW: Object Daten-Layer
 
Wenn id den PK darstellt, dann würde ich die Property ReadOnly deklarieren, da der Wert ja vermutlich über einen Trigger und Generator befüllt wird statt durch Zuweisung.

Sir Rufo 14. Feb 2011 10:37

AW: Object Daten-Layer
 
Wieso, hängt von der Anwendung ab und Update dann so
Code:
UPDATE MeineTabelle SET id = :NewID WHERE id = :OldID

DeddyH 14. Feb 2011 10:43

AW: Object Daten-Layer
 
Ich gehe davon aus, dass ID ein künstlicher Schlüssel (PK) ist, der vom DBMS generiert wird. Den würde ich in den meisten Fällen nicht überschreiben wollen, mir fällt auch kein guter Grund für ein solches Überschreiben ein. Deshalb mein Vorschlag einer ReadOnly-Property, es sei denn, es ist ganz anders gemeint bzw. gewollt.

Sir Rufo 14. Feb 2011 11:01

AW: Object Daten-Layer
 
Zitat:

Zitat von DeddyH (Beitrag 1081533)
Ich gehe davon aus, dass ID ein künstlicher Schlüssel (PK) ist, der vom DBMS generiert wird. Den würde ich in den meisten Fällen nicht überschreiben wollen, mir fällt auch kein guter Grund für ein solches Überschreiben ein. Deshalb mein Vorschlag einer ReadOnly-Property, es sei denn, es ist ganz anders gemeint bzw. gewollt.

Es kommt halt drauf an, wer die Datenhoheit hat ;)
Und in seinem Beispiel war id nicht ReadOnly

hanspeter 14. Feb 2011 11:25

AW: Object Daten-Layer
 
Die ID ist nur ein Beispiel. Im konkreten Fall wird diese von einem Datenbank-Generator erzeugt und ist readonly.
Die Idee mit einer Stringlist würde bedeuten, dass ich alle Variablen in string wandeln müsste. Also auch z.B. boolean.
Die Idee mit dem Array of Variant ist überlegenswert, bringt allerdings mehr Speicheroverhead mit. Geschwindigkeit sollte im Datenbankbereich hier
vernachlässigbar sein.

Bei meinem Präfix "f" gibt es wohl ein kleines Missverständnis.
Ich wollte die Property so wie in der Datenbank benennen und die zugehörige private Variable dann mit einem Präfix versehen.
In C# geht das hier etwas eleganter.
Ich hatte auch bereits einmal probiert für jedes Field ein TField zu verwenden, das ist aber ein ziemlicher Overhead. Den Feldnamen über RTTI zu bestimmen ist hier eleganter.

Im Moment baue ich wie gesagt einen kleinen Generator, der mir aus einer Kommentarzeile den Quelltext der Klasse (die Propertys) generiert.

Beispiel : { Field= ID,NAME,VORNAME,PLZ,ORT,STRASSE,TELEFON TABLENAME= Archiv PrimaryKey= ID}

Mit einem Array of Variant müsste ich dann noch aus der Query die Typinformation des Feldes auslesen und abspeichern.

Kleiner Nachtrag.
Kann ich über RTTI eigentlich den Feldindex der Propertie auslesen?


Gruß
Peter


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz