[Delphi] Database classes

Pagina: 1
Acties:
  • 103 views sinds 30-01-2008
  • Reageer

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ik had het volgende idee over classes om de database gegevens te beheren. Maar ik wilde weten of dit wel klopt en volledig is. Het gaat hier alleen om een opzet.

Ik heb hier alleen het adressen systeem inzitten, mocht dit kloppen dan voer ik het helemaal door. De overige gegevens heb ik weggelaten in deze post omdat het alleen om adressen gaat.

Een relatie (waar meerdere adressen in kunnen zitten):
code:
1
2
3
4
5
6
7
8
9
TRelatie = class(TObject)
public
  Adressen: TListAdressen;

  function New: TRelatie;  //Maakt een nieuwe relatie aan
  procedure Delete;  //Verwijderd de relatie uit de database
  procedure Load(RelatieId: Integer);  //Laadt alle relatie gegevens
  procedure Save;  //Slaat de relatie gegevens op in de database
end;

Een lijst van adressen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
TListAdressen = class(TObject)
private  
  List: ObjectList;  //Lijst met TAdres objecten
  RelatieId: Integer;  //Bij welke relatie horen deze adressen
public  
  procedure Load(RelatieId: Integer);  //Laad alle adressen van een relatie
  function New: TAdres;  //Maakt een leeg adres en voegt deze toe aan de lijst
  procedure Delete(Index: Integer);  //Verwijder een adres uit de lijst
  procedure Save;  //Sla de lijst van adressen op in de database
  function Item(Index: Integer): TAdres;  //Geeft een adres terug
  property Count: Integer read GetCount;  //Hoeveel adressen zijn er
end;

Een adres:
code:
1
2
3
4
5
6
7
8
9
10
TAdres = class(TObject)
public
  AdresId: Integer;
  Straat: String;
  Postcode: String;
  Woonplaats: String;
  
  procedure Load(AdresId: Integer);  //Laadt een adres uit de database
  procedure Save;  //Slaat een adres op in de database
end;


Roep je dus de Save procedure aan van de TRelatie, dan zal die de Save aanroepen van TListAdressen welke voor elk TAdres object weer de Save aanroept.

Is deze opzet een beetje goed zo. Heb ik overal aangedacht qua adressen?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • whoami
  • Registratie: December 2000
  • Nu online
Hmmm.... Beetje moeilijk om te zeggen of je iets vergeten bent of niet.
Het ontwerp ziet er wel ok uit denk ik, ik gebruik momenteel zelf ook ongeveer zo'n class-structuur.

Voor iedere tabel in m'n databank heb ik een class (afgeleid van een base-class). Die base-class heeft 2 abstracte functies nl. Load en Save.... Een beetje hetzelfde dus als bij u.

https://fgheysels.github.io/


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Oja, een base class komt er zeker weten, waar ook een property Changed (boolean) in komt. Maar het gaat mij meer om de lijst met adressen. Dit is in dit geval een voorbeeld, maar het komt vaker voor in de database dat ik lijsten moet hebben. Is dat zo een goed systeem? Wordt dit zo vaker toegepast door bedrijven?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • whoami
  • Registratie: December 2000
  • Nu online
Als ik (in mijn situatie) een lijst wil hebben van bepaalde gegevens, dan return ik een dataset of een arraylist. (Ik ben bezig op m'n werk in .NET, vandaar).

De functie die dat verzorgt is een static function binnen de class 'Adressen' bv. Eigenlijk is het te zien wat je precies wilt doen met de verkregen records.

Wat houdt een 'relatie' bij jou precies in?

https://fgheysels.github.io/


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
whoami schreef op 03 oktober 2002 @ 20:58:

[..]

Wat houdt een 'relatie' bij jou precies in?
In dit voorbeeld? Een relatie is een persoon (record) met 0 of meer adressen.

Het gaat niet zozeer om een relatie, dat is in het voorbeeld gebruikt. Het gaat me meer om hoe ik in Delphi een class maak welke een record vertgenwoordigd in de database met een lijst eraan. Dus een relatie met 0 of meer adressen.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • whoami
  • Registratie: December 2000
  • Nu online
Delphi schreef op 03 oktober 2002 @ 21:07:
[...]

In dit voorbeeld? Een relatie is een persoon (record) met 0 of meer adressen.

Het gaat niet zozeer om een relatie, dat is in het voorbeeld gebruikt. Het gaat me meer om hoe ik in Delphi een class maak welke een record vertgenwoordigd in de database met een lijst eraan. Dus een relatie met 0 of meer adressen.


Ah zo....
Ik doe het ook zo. In de class gewoon een arraylist of TList oid bijgehouden die alle gerelateerde objecten van een andere class bijhoudt.

https://fgheysels.github.io/


  • Delphi32
  • Registratie: Juli 2001
  • Laatst online: 30-08 10:13

Delphi32

Heading for the gates of Eden

Even insectenvrijen:
1. Load, New, Save, Delete zijn geen operaties die je op een TRelatie hoort uit te voeren. Het zijn helper functions (tbv persistency) en ik zou die liever in een manager-class stoppen.
2. Behalve een property Changed (blij dat je er zo vroeg achter was dat je die nodig had) ga je zeker behoefte hebben aan een OnChanged event, dus stop die maar vast in je base class :)
3. Het voorgestelde mechanisme werkt uitstekend (wij werken ook met een vergelijkbaar systeem), maar hou er rekening mee dat het maken van een lijst van items uit een stel records aardig wat overhead met zich mee kan brengen als je de conversie record-TObject gaat uitvoeren voor grotere hoeveelheden data.
4. Ook is het bijzonder lastig dat je een heleboel db-aware functionaliteit gaat verliezen. Tenzij je ervoor kiest om db-aware componenten links te laten liggen, maar dan ga je vast behoefte krijgen aan object-aware componenten. Waarmee je je a. een heleboel extra werk op de hals haalt, en b. buiten de platgetreden Delphi-paadjes begeeft. Dat laatste is op zich nog niet zo erg, maar wel bijzonder vervelend als je besluit andere controls te gaan gebruiken. Ben ik nu te vaag? Hoop het niet :)

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
1. Die methods moet ik dus uit de TRelatie halen en apart nemen. Dan wordt het dus bijvoorbeeld:
function LoadRelatie(RelatieId: Integer): TRelatie;

Kan. Maar ik ging er vanuit dat er altijd een TRelatie object is, genaamd Relatie. Dan kun je dus maar 1 relatie tegelijk inladen wat eigenlijk ook de bedoeling is.
Naar mijn idee is jou oplossing wel netter.

2. Events komen er idd: OnChanged, OnSaved, OnLoaded. Lijkt me wel makkelijk.

3. Snap ik niet helemaal. Je doelt op die TObjectList? Is daar een betere oplossing voor dan?

4. Ben ik me geheel van bewust. Sowieso geen DB-aware componenten. Dus alles handmatig (= meer controle).

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • Delphi32
  • Registratie: Juli 2001
  • Laatst online: 30-08 10:13

Delphi32

Heading for the gates of Eden

Delphi schreef op 03 oktober 2002 @ 23:50:
3. Snap ik niet helemaal. Je doelt op die TObjectList? Is daar een betere oplossing voor dan?
Situatie: een relatie met 100.000 adressen. Absurd maar het gaat om het voorbeeld. Ok, ik ga de relatie laden. Die gaat dan alle adressen uit de database laden. Elk adres-record dat ik in mn dataset vind, transformeer ik in een TAdres dat in de ListAdressen komt. Duidelijk?
Dat gaat bij grotere hoeveelheden gegevens een hoop tijd kosten, heb ik gemerkt.
4. Ben ik me geheel van bewust. Sowieso geen DB-aware componenten. Dus alles handmatig (= meer controle).
Handmatig meer controle? Het ligt er maar aan wat je gewend bent. Persoonlijk vind ik dat de db-aware componenten zeer veel controle-mogelijkheden hebben: rijen events op de control zelf, daarnaast op dataset level nog het een en ander, en dan ook nog de TField-eigenschappen en events die de correctheid van de waarden van het veld in de gaten houden. Toegegeven, als je niet oppast loopt je code daarmee uit de hand ('waar stond de controle voor de geldigheid van die waarde ook al weer? Was dat OnChange, OnClick van het control, of OnChange van het Field, of OnBeforePost, enz') en bepaalde db-controls geven qua uiterlijk niet de functionaliteit die ik nodig heb.
Maar! Als je netjes omgaat met de db-aware components (bv geen validatie in de forms, en vaste definities voor wat elk event mag gaan doen), vermindert dat je ontwikkeltijd nogal drastisch, en het scheelt jou als ontwikkelaar een eindeloze berg typewerk. Dat is mij best wat waard.

Mijn conclusie luidt dan ook: het zou het beste zijn, als er een TDbObject-achtig iets kwam, wat voor iedereen beschikbaar is, wat kan werken met de db-aware components, en wat niet onderdoet voor de TDataset-route qua performance. Nu zie en hoor ik her en der bedrijven die een Object-Relational mapping gaan bouwen, niemand heeft een standaard daarvoor, dus iedereen rotzooit maar wat aan (NOFI Delphi!). Zolang een dergelijk object-framework niet voor iedereen algemeen beschikbaar is, bekijk ik per geval wel of ik db-componenten wil gebruiken of juist niet.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Oke, bedankt voor de op- en aanmerkingen. Ik ga het in ieder gaval uitwerken en er nog wat over nadenken natuurlijk.

Bedankt tot zover. :)

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 30-08 09:55

Creepy

Tactical Espionage Splatterer

En ik vraag me af waarom je in TListAddress een een TObjectList gebruikt i.p.v. een sub-klasse van TObjectLIst te maken?

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • whoami
  • Registratie: December 2000
  • Nu online
Creepy schreef op 04 oktober 2002 @ 09:10:
En ik vraag me af waarom je in TListAddress een een TObjectList gebruikt i.p.v. een sub-klasse van TObjectLIst te maken?


Waarom zou je er een subclass van maken?

https://fgheysels.github.io/


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 30-08 09:55

Creepy

Tactical Espionage Splatterer

procedure Load(RelatieId: Integer); //Laad alle adressen van een relatie
function New: TAdres; //Maakt een leeg adres en voegt deze toe aan de lijst
procedure Delete(Index: Integer); //Verwijder een adres uit de lijst
procedure Save; //Sla de lijst van adressen op in de database
function Item(Index: Integer): TAdres; //Geeft een adres terug
property Count: Integer read GetCount; //Hoeveel adressen zijn er
Count, items, Delete, new (ongeveer gelijk aan add) zijn functionaliteiten die je rechtstreeks kan overerven.
Met erg weinig code heb je snel en makkelijk een subklasse van TObjectList

vb:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  THoofdstukList = class(TObjectList)
  private
    function Getitems(index: integer): THoofdstuk;
    procedure Setitems(index: integer; const Value: THoofdstuk);
  public
        property items[index: integer]: THoofdstuk read Getitems write Setitems; default;
  end;

function THoofdstukList.Getitems(index: integer): THoofdstuk;
begin
     result:=inherited get(index);
end;

procedure THoofdstukList.Setitems(index: integer; const Value: THoofdstuk);
begin
     inherited put(index, value);
end;

Deze code is dus genoeg om een eigen objectlist te maken. Zul je alleen nog de load en save bij moeten maken. Functies zoals add, delete, insert, remove etc zitten standaard al in een TObjectList. Hoef je ook geen wrapper functies voor te maken wat je wel moet doen als je een TObjectList gebruikt in je klasse i.p.v. een subklasse maken.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Nog even een vraagje: moet ik voor elke variable (database kolom) een Set procedure maken om de Change value op True te zetten.
Sommige tabellen hebben namelijk aardig wat kolommen en dat zou toch wel veer werk worden.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • whoami
  • Registratie: December 2000
  • Nu online
Delphi schreef op 11 oktober 2002 @ 14:50:
Nog even een vraagje: moet ik voor elke variable (database kolom) een Set procedure maken om de Change value op True te zetten.
Sommige tabellen hebben namelijk aardig wat kolommen en dat zou toch wel veer werk worden.


Wat is de bedoeling daar dan van? Dat je enkel voor de gewijzigde velden van het record een update doet?
Dat vergt toch veel werk en overhead dat het eigenlijk beter is imho om, van zodra er één veld gewijzigd is alle velden te updaten. Dan heb je maar één update nodig (van evenwel alle velden), maar dat gaat toch even snel gaan als een update van 1 veld.
Anders moet je nog dynamisch uw update-query gaan opbouwen of voor ieder gewijzigd veld een update doen, en dat laatste gaat traag zijn.

Ik zou dus maar één changed property maken die op het ganse record van toepassing is, en van zodra er één veld gewijzigd wordt, zet je die changed property op true. Als je dan updated, ga je gewoon alle velden van dat record updaten.

https://fgheysels.github.io/


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Het is zeker niet de bedoeling om per veld te gaan updaten!

Maar ik wil weten wanneer een record is veranderd. Een record is veranderd (dus Changed property op True zetten) als een veld is veranderd. Ik moet dus per veld checken of deze veranderd, zoja: Changen := True. Maar dan moet ik dus voor elk veld een Setter maken welke de property veranderd van dat veld EN de changed op True zet. Dit wordt dus per veld (kolom) een property en een Set procedure. Veel code (typ werk) dus.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • whoami
  • Registratie: December 2000
  • Nu online
Je kunt eigenlijk op verschillende manier gaan checken of een record veranderd is of niet.

Als je de gegevens enkel kunt wijzigen via een form bv, dan kun je de OnChange event van de TextBoxen, ComboBoxen etc gaan gebruiken om die Changed property op true te zetten van het record.
Wat je ook kunt doen, is de oude waarden van de velden in uw record gaan bewaren en deze dan vergelijken.

https://fgheysels.github.io/


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 30-08 09:55

Creepy

Tactical Espionage Splatterer

Delphi schreef op 11 oktober 2002 @ 15:33:
Het is zeker niet de bedoeling om per veld te gaan updaten!

Maar ik wil weten wanneer een record is veranderd. Een record is veranderd (dus Changed property op True zetten) als een veld is veranderd. Ik moet dus per veld checken of deze veranderd, zoja: Changen := True. Maar dan moet ik dus voor elk veld een Setter maken welke de property veranderd van dat veld EN de changed op True zet. Dit wordt dus per veld (kolom) een property en een Set procedure. Veel code (typ werk) dus.
Tik al je propertys op deze manier in:

Delphi:
1
2
3
4
5
6
7
TMyObject
  public:
   property veld1: integer;
   property veld1: string;
   property veld2: string;
   property veld3: string;
end;

Druk dan op ctrl+shift+c. De TMyObject code word aangevult met voor elke property een private property en een Set procedure, inclusief de implementatie van de Set procedure 8) Hoef je alleen nog de Fchanged:=true o.i.d. in te vullen.

Ik kan je afraden om in de onchange events van edit velden e.d. de changed property van je object bij te houden. Het is erg namelijk erg makkelijk om 1 keer een changed te vergeten (dit zeg ik helaas uit ervaring). Ook als je ergens een keer in code een property aanpast moet je er elke keer weer een object.changed:=true bijzetten.

Ook kan je in de Set procedure de nieuwe waarde met de oude vergelijken om te kijken of de waarde ook ECHT is veranderd.
Delphi:
1
2
3
4
5
6
7
8
procedure TMyObject.Setveld2(const value: string);
begin
   if value<>FVeld2 then
   begin
      fchanged:=true;
      FVeld2:=value;
   end;
end;

Dan hoef je ook niet twee keer al je properties bij te houden om ze te kunnen vergelijken of ze ook daadwerkelijk aangepast zijn wat met een redelijk DB toch aardig geheugen kan vreten.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • MaxxRide
  • Registratie: April 2000
  • Laatst online: 09-01 10:13

MaxxRide

Surf's up

Je kunt voor een lijst van objecten natuurlijk gewoon een TStringlist oid gebruiken. Dat werkt makkelijk en heeft een redelijke implementatie binnen delphi.
Je kunt er ook zelf een gelinkte lijst voor schrijven met wat snelle zoekfunctionaliteiten.

If you are not wiping out you are nog pushing enough...


  • whoami
  • Registratie: December 2000
  • Nu online
Pinda schreef op 12 oktober 2002 @ 12:01:
Je kunt voor een lijst van objecten natuurlijk gewoon een TStringlist oid gebruiken. Dat werkt makkelijk en heeft een redelijke implementatie binnen delphi.
Je kunt er ook zelf een gelinkte lijst voor schrijven met wat snelle zoekfunctionaliteiten.


Een TObjectList bedoel je. ;)

https://fgheysels.github.io/


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 30-08 09:55

Creepy

Tactical Espionage Splatterer

Pinda schreef op 12 oktober 2002 @ 12:01:
Je kunt voor een lijst van objecten natuurlijk gewoon een TStringlist oid gebruiken. Dat werkt makkelijk en heeft een redelijke implementatie binnen delphi.
Je kunt er ook zelf een gelinkte lijst voor schrijven met wat snelle zoekfunctionaliteiten.
Wedden dat de TOBjectList (of een eigengemaakt descendant daarvan) sneller is dan jou gelinkte lijst? ;)

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 12:54

Tomatoman

Fulltime prutser

Delphi schreef op 03 oktober 2002 @ 21:07:
[...] Het gaat me meer om hoe ik in Delphi een class maak welke een record vertgenwoordigd in de database met een lijst eraan. Dus een relatie met 0 of meer adressen.
Daarvoor heb je in Delphi de abstracte class TCollection, die een lijst met 0 of meer TCollectionItem descendants beheert. Daarmee krijg je zoiets:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
TRelatie = class(TComponent)
  FAdressen: TListAdressen;
public
  Adressen: TListAdressen read FAdresssen;
  ...
end;

TListAdressen = class(TCollection)
{ TCollection class overriden, zodat hij een aantal TAdres items beheert }
end;

TAdres = class(TCollectionItem)
{ TCollectionItem class overriden }
end;

Een goede grap mag vrienden kosten.


  • MaxxRide
  • Registratie: April 2000
  • Laatst online: 09-01 10:13

MaxxRide

Surf's up

Creepy, wedden :)

Ik weet niet precies hoe snel die lijsten zijn, ik weet alleen wel dat ik in mijn compressie algo eerst gebruik maakte van de delhi stringlist. het algoritme nam 5 min. (!) in beslag. Met een zelfgemaakt binaire boom ging het aanzienlijk sneller!!!

If you are not wiping out you are nog pushing enough...


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 30-08 09:55

Creepy

Tactical Espionage Splatterer

Pinda: Ik heb het ook niet over de TStringList, maar over de TObectList ;)
Ik vraag me af hoe jij de TStringList hebt gebruikt...

Enneuh... een stringlist vergelijken met een binaire boom is appels met peren verelijken he. Als je nu een gelinkte lijst met een TObjectList gaat vergelijken is het wat eerlijker aangezien de structuur hetzelfde is.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney

Pagina: 1