Page 1 of 10 1 2 3 ... LastLast
Results 1 to 15 of 144

Thread: Business Objects - het begin.

  1. #1
    Delphi & OO in Vlaanderen SamWitse's Avatar
    Join Date
    Sep 2007
    Location
    Brussel
    Posts
    833

    Post Business Objects - het begin.

    Werken met DataCoponenten TTable, TDataSource, en de daaraan gekoppelde TDBEdit TDBGrid en de hele familie, is geweldig in Delphi. Nergens heb ik met zoÔÇÖn gemak met databases gewerkt.
    Allemaal mooi, tot de dag dat je graag wil afwijken van die standaarden, en je ÔÇÿeigenÔÇÖ manier wil opleggen. Wat kun je doen?

    Hier al een begin van Business Objects, en hoe je die in jouw geval kunt gebruiken.

    Delphi heeft het grandioze voordeel dat je er in RAD-mode (Rapid Application Development) kunt werken. Eigenlijk werken de meesten van ons zo. Snel componenten droppen op forms, wat code in button-events steken, en je applicatie werkt. Dit werkt heel goed. Het is een plezier om zo met Delphi te werken.
    Wens je af te wijken van de standaard manier waarop de componenten werken, dan dien je andere componenten te zoeken, kopen of je moet zélf klassen of componenten maken.

    Eén van de belangrijke redenen om zelf klassen te schrijven, is dat je hetgeen in je database zit, niet eenvoudigweg via zogenaamde ÔÇÿdata-awareÔÇÖ componenten op je scherm te toveren is. Je wil je data omvormen, je wil je data tussentijds in je applicatie gebruiken, je dient berekeningen en controleÔÇÖs uit te voeren. Of omgekeerd, je wil je editvelden ook voor andere dingen gebruiken dan voor het afbeelden van data: het zoeken van data bijvoorbeeld.

    Een eerste manier om in te grijpen volgt hier.

    Je kunt een aantal omvormingen en ÔÇÿtruukenÔÇÖ toepassen in de OnGetText en OnSetText-events van fields.Wil je het klantnummer bijvoorbeeld afbeelden in een TPanel, dan selecteer je eerst het TField waarin het klantnummer staat, en dan zeg je in zijn OnGetText dat je de waarde in Panel1 zet.

    • Klik met je rechter muisknop op je TTable-component, en je krijgt een context-menu.
    • Selecteer Fields Editor
    • Klik met de rechter muisknop in het witte vak, en selecteer All Fields
    • Nu zie je alle velden van je TTable.
    • Klik op het veld waarin je klantnummer staat (vb. KlantNummer)
    • In de Object Inspector zie je nu een TNumberField voor je klantnummer.
    • Maak een handler voor het OnGetText-event, door het tabblad Events te kiezen, en naast OnGetText te dubbelklikken.
    In deze handler zeg je wat je met de waarde van het veld gaat doen als het TField vanuit de database opgevuld wordt:


    Code:
    procedure TFormF.MijnFieldGetText(Sender: TField; var Text: string;  DisplayText: Boolean);
    begin  
      Panel1.Caption := sender.asstring
    end ;
    Zet de property visible van het TEditField dat met het field KlantNummer verbonden is, op false. Je ziet de TDBEdit (in runtime) niet meer, maar wel je TPanel, waar je klantnummer op afgebeeld wordt.

    Op deze manier beeld je je gegevens anders af dan gewoon rechtstreeks in TDBEdits.
    Je kunt natuurlijk de waarden in een TEdit afbeelden in plaats van in een TPanel, zoals hierboven.

    Omgekeerd kun je hetgeen je in TEdits steekt, copiëren naar TDBEdits.

    In de OnExit-event copieer je hetgeen ingetikt hebt, naar de overeekomstige TDBEdit. Dus je maakt een TEdit voor je klantnummer, en in de OnExit schrijf je het volgende:

    Code:
    procedure TForm1.Edit1Exit(Sender: TObject);
    begin
    Table1Klantnummer.AsString := (Sender as TEdit).Text
    end;
    Dien je nog controles uit te voeren of een klantnummer geldig is, dan doe je dit in de OnExit:

    Code:
    if StrToInt ((Sender as TEdit).Text) > 0 then
    Table1Klantnummer.AsString := (Sender as TEdit).Text
    Gebruik je nu dit editveld om te zoeken als je in een ÔÇÿZoekmodeÔÇÖ staat, dan zet je dit ook in de OnExit:

    Code:
    if not InZoekMode then
     if StrToInt ((Sender as TEdit).Text) > 0 then    
       Table1Klantnummer.AsString := (Sender as TEdit).Text
    In de zoekbutton schrijf je dan de code die een query maakt met hetgeen in het editveld is ingetikt, bijvoorbeeld:

    Code:
      Query1.Params[0].AsString := (Sender as TEdit).Text ;
      Query1.ExecSQL ;
    Hoewel deze methode werkt, is dit om twee redenen geen ideale oplossing. Ten eerste moet je een omweg maken via onzichtbare TDBEdits. Niet echt een ÔÇÿpropereÔÇÖ manier van werken. Ten tweede staat jouw kennis over een klantnummer in het OnExit-event van een TEdit. Wat als je een klantnummer nog elders wil gebruiken? Terug diezelfde code die in het OnExit-event staat, copiëren? Of simpelweg vergeten?

    Als we nu eens alle data en logica van een klant eens in een klasse zouden zetten? Is dit niet de bedoeling van klassen? Dan maak je in die klasse alle ÔÇÿlogicaÔÇÖ die je kent van een klant. Verder maak je een method om een klant te zoeken, en één om een klant weg te schrijven, en je hebt je eerste 'Business Object'. Je mag van die klant een component maken, maar dat hoeft helemaal niet. Je klasse zou er zo kunnen uitzien:

    Code:
    unit KlantUnit ;
     
    interface
    type
    TKlant = class (TObject)
    private  
      FNummer : integer ;
      FFirmanaam: string ;
      FTable: Ttable ;
      procedure SetNummer ( value: integer ) ;
    public
      constructor Create ;
      destructor Destroy ;
      function Post : Boolean ;
      function Zoek: Boolean ;
      property Nummer : integer read FNummer write SetNummer ;
      property Firmanaam: string read FFirmanaam write FFirmanaam ;
      property Table: TTable read FTable write FTable;
    end ;
     
    implementation
     
    procedure TKlant.SetNummer ( value: integer ) ;
    begin
      if value > 0 then { hier zit mijn 'business logica'; enkel en alleen hier}    
        FNummer := value
    end ;
     
    function TKlant.Post : Boolean ;
    begin
      if assigned (FTable) then
    {wellicht zul je hier wat meer testen willen toevoegen om na te gaan of je de dataset wel kunt gebruiken}
        result := FTable.InsertRecord ( [FNummer, FFirmanaam] )
    end ;
     
    function TKlant.Zoek: Boolean ;
    begin
      if assigned (FTable) then
        result := FTable.FindKey([FNummmer])
     end ;
    Door een Table te koppelen aan je klant, kan een klant zichzelf wegschrijven.

    In je pogramma kun je nu een (of meerdere) klant-object maken en gebruiken. Dit noemt men het koppelen van de GUI aan de business objecten. Bijvoorbeeld:

    Code:
    uses  KlantUnit;
    var
    SchermKlant : TKlant ;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      SchermKlant := TKlant.Create ;
      SchermKlant.Table := Table1
    end ;
     
    procedure Form1.ZoekButtonClick (Sender: TObject);
    begin
      SchermKlant.Nummer := Edit1.Text ;
       if SchermKlant.Zoek then
         Edit2.Text := SchermKlant.FirmaNaam 
    end ;
    Je ziet dat je in je form geen rechtstreeks gebruik meer maakt van datasets, maar dat je in je form business objecten gebruikt, die op hun beurt datasets aanspreken. Dat is het hele idee van de ÔÇÿbusiness object layerÔÇÖ.
    Maar je mist iets: Alle mooie DataControls, zoals TDBEdit, TDBGrid en zo meer, kun je niet meer gebruiken. Deze link je namelijk aan een TDataSource, die op zijn beurt aan een TTable (of andere) gelinkt is. En een TDataSource kun je niet aan een TKlant linken.
    Dus, een TKlant gebruiken met je datacontrols? Vergeet het maar!

    Een andere manier om een ÔÇÿdata-awareÔÇÖ business klasse te maken, is ze rechtstreeks af te leiden (inheriten) van TTable of TDBDataSet.Bijvoorbeeld:

    Code:
    TKlant = class (TTable) ;
    
    In dit geval ga je zelf TFields maken met velden die niet ÔÇôof anders- weggeschreven worden in de database. Het voordeel is dat je de data-aware components (zoals een TDBEdit, TDBGrid, etc.) gewoon kunt blijven gebruiken door ze te linken aan je zelfgemaakte TTables.

    Ik heb dit ooit, met vallen en opstaan, maar uiteindelijk met succes gedaan. Ik zal eens diep in mijn archieven moeten zoeken om terug uit te pluizen wat je hiervoor allemaal moet doen. En dit toetsen aan de kennis die ik ondertussen bijgekregen heb. Daarna zal ik een vervolg breien op dit artikel.

    Of mocht iemand anders met dit artikel 'Het Licht' gezien hebben, en dit willen neerschrijven in een vervolgartikel, hou je vooral niet in!

    Sam Witse.
    Last edited by SamWitse; 23-Nov-07 at 22:11.
    Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration.

    Sam Witse.
    Delphi & OO in Vlaanderen

  2. #2
    Ik heb hier al een stukje over geschreven, ondermeer:

    http://www.nldelphi.com/Forum/showpo...84&postcount=5

    Het idee waarover je schrijft, is in principe niet nieuw, maar bestaat al lang. Er bestaan een groot aantal technieken om je doel te bereiken, en de verzamelnaam wordt 'data-abstractie' genoemd. Dit is een verzameling van een aantal technieken waaronder AOL ( Abstract Object Layer.

    Laat me eerst toe om de basistechniek te gaan uitleggen ( en daarmee ook de relatie met ondermeer de bussiness rules) straks leg ik dan uit wat je kunt doen met je Db-Aware componenten.
    In de meest eenvoudige vorm, bestaat de techniek er in om een object te gaan afleiden van bijvoorbeeld een TTable:

    Code:
    type
    TKlant = class (TTable)
    private
      FNummer : integer ;
      FFirmanaam: string ;
        function GetFirmanaam: string;
        function GetNummer: integer;
        procedure SetFirmanaam(const Value: string);
        procedure SetNummer(const Value: integer);
    public
      property Nummer : integer read GetNummer write SetNummer ;
      property Firmanaam: string read GetFirmanaam write SetFirmanaam ;
    end ;
    
    implementation
    
    { TGame }
    
    
    function TKlant.GetFirmanaam: string;
    begin
      Result:= FieldByName('FirmaNaam').asstring;
    end;
    
    function TKlant.GetNummer: integer;
    begin
      Result:= FieldByName('Nummer').asstring;
    end;
    
    procedure TKlant.SetFirmanaam(const Value: string);
    begin
      FieldByName('FirmaNaam').asstring:= Value;
    end;
    
    procedure TKlant.SetNummer(const Value: integer);
    begin
      FieldByName('Nummer').asinteger:= Value;
    end;
    
    end.
    in je code kun je er mee gaan spelen, zoals het beschrijven van de tabel met een waarde.

    Code:
    begin
      SchermKlant.Nummer := Edit1.Text ;
    end;
    Dit heeft dezelfde betekenis als:
    SchermKlant.FieldByName('Nummer').Asstring := Edit1.Text ;
    Het tabel-object heeft volgende eigenschappen:
    1. De tabel beschrijven en lezen gebeurt door middel van het object. Dit betekent dat de tabel-data enkel bereikbaar is ( en kan beschreven worden ) door het object. Dit laat toe om ondermeer
    a. beperkingen ( constraints ) in te bouwen in het object zelf.
    b. read-only velden te maken door bijvoorbeeld de setter weg te laten
    c. code te schrijven die werkt zoals een trigger
    d. ....

    2. Alle procedures en functies die te maken hebben met dit tabel-object kunnen hier geschreven worden ( bijvoorbeeld een record-count)

    Maar dit is lang niet alles. Niet enkel de relaties binnen een tabel kunnen worden opgevangen, maar op een eenvoudige manier ook inter-tabel relaties door de objecten op een dynamische manier te gaan koppelen. Deze techniek noemt men dynamische encapsulatie.

    Code:
      property Firmanaam: string read GetFirmanaam write SetFirmanaam ;
      property Facturen: TFacturen read GetFacturen;
    end ;
    
    implementation
    
    { TGame }
    
    
    function TKlant.GetFacturen: TFacturen;
    begin
      if not assigned(FFacturen)
      then FFacturen:= TFacturen.CreateNoFetch;
    
      FFacturen.SetIdKlantNr(Nummer);
      Result:= FFacturen;
    end;
    Via een filter ga ik aan mijn klant alle facturen gaan koppelen.

    Code:
    Klant.ZoekNummer('Edit1.Text');
    
    while not Klant.Facturen.Eof do
    begin
      if Klant.Facturen.NognietAfgedrukt
      do DrukFactuurAf(Klant.Facturen.FactuurNummer);
      Klant.Facturen.Next;
    end;
    Deze techniek heeft de volgende eigenschappen:

    Het tabel-object heeft volgende eigenschappen:
    1. Alle data-objecten ( behalve één ) worden volledig automatisch gecreeerd.
    2. Alle data-objecten worden pas gecreeerd wanneer ze nodig zijn ( niet vroeger )
    3. De relatie tussen beide is automatisch ( zoals een Master-slave relatie ) en is intern in het object

    Wat er hier staat is slechts de 'prille basistechniek'. In werkelijkheid zijn de meeste systemen een groot stuk geavanceerder.

    De Db-Aware componenten koppelen kan in principe via een aantal basistechnieken.

    1. In het eerste geval wordt een Db-Aware component gebruikt die wordt gekoppeld met de dataset om te lezen, maar niet om te schrijven. Het component volgt alle veranderingen van de dataset, maar schrijft zelf geen veranderingen weg. Bij een on-Exit event wordt de data via de property naar het juiste veld geschreven en van daar uit naar de databank.

    2. Er wordt gebruik gemaakt van een Memory-dataset ( bijvoorbeeld een TClientDataset ). De wijzigingen worden via de properties in de memory-dataset geschreven. De business rules die in het object vervat zitten controleren de data, en zorgen voor de fout-afhandeling. Via een soort COMMIT commando worden ze weggeschreven naar de databank.

    3. Er wordt gebruik gemaakt van een Memory-dataset ( bijvoorbeeld een TClientDataset ) met rechtstreeks daarop gekoppeld de Db-Aware componenten. De koppeling tussen de Memory-dataset en de databank gaat via de properties van de objecten.

    In al deze bovenstaande technieken wordt soms ook RTTI gebruikt.

    Het leuke van het verhaal is, dat deze techniek ook een RAD-techniek is en op een eenvoudige manier kan gekoppeld worden aan bijvoorbeeld UML. Ik heb ondermeer een codegenerator die gewoon alle gegevens ( velden, keys, relations ) uit een database gaat lezen en de code schrijft voor alle Dataobjecten ( met hun relaties onderling ). Zelfs voor een paar honderd tabellen is je volledig basis data-framework klaar in een half uur, meestal volledig foutloos en voorzien van de nodige try-excepts, maar ook van een aantal andere features ( zoals MailOnError, Trace-files, logfiles, ... ).

    Wat hier nu staat is in principe heel kort uitgelegd. De grote voordelen zijn in principe:
    1. De ontwikkeling gaat sneller want de code wordt automatisch aangemaakt
    2. De code is veel leesbaarder
    3. Alle code en gegevens betreffende het object worden ook daar bewaard ( en dat is het echte OO-principe ) waardoor de code een groot stuk gestructureerder is en veel beter onderhoudbaar.
    Last edited by rik.osstyn; 24-Nov-07 at 00:51.
    De verbazing begint waar de kennis ophoudt

  3. #3
    En volgens mij zijn de mannen van Borland / Codegear ook in deze richting gaan denken en hebben ze dat ECO genoemd.

  4. #4
    Het basis-idee is reeds een 10-tal jaren oud. De basisprincipes komen trouwens van één en dezelfde bron. Alleen de uiteindelijk implementatie ( en de naamgeving ) verschilt.
    De verbazing begint waar de kennis ophoudt

  5. #5
    Alleen de uiteindelijk implementatie ( en de naamgeving ) verschilt.
    Is dat niet zo met bijna alles wat wij ITers bijna iedere dag doen?

    Nooit gedacht dat ik me nog ooit met geschiedenis zou gaan bezighouden

  6. #6
    Is dat niet zo met bijna alles wat wij ITers bijna iedere dag doen?
    Tja, uiteindelijk wel. Persoonlijk ken ik een 6-tal bedrijven die het bovenstaande principe toepassen, maar het zullen er ongetwijfeld meer zijn, want het wordt vooral gebruikt door grote bedrijven.

    Het leuke van het verhaal is, dat de gebruikte objecten verdacht goed lijken op het voorbeeld hierboven:

    Code:
    type
    TKlant = class (TObject)
    private  
      FNummer : integer ;
      FFirmanaam: string ;
      FTable: Ttable ;
      procedure SetNummer ( value: integer ) ;
    public
      constructor Create ;
      destructor Destroy ;
      function Post : Boolean ;
      function Zoek: Boolean ;
      property Nummer : integer read FNummer write SetNummer ;
      property Firmanaam: string read FFirmanaam write FFirmanaam ;
      property Table: TTable read FTable write FTable;
    end ;
    Er zijn wel een aantal aanpassingen.

    1. Het object gaat men nooit gaan afleiden van TObject, maar van een eigen basis-object. In dat object zit tal van gemeenschappelijke methods en procedures, bijvoorbeeld:
    - Trace mogelijkheden
    - Eigen fout-afhandelingen ( zoals Mail-on-error, SMS-on-Error, Write-to-log, write-to-db, ... )

    2. Een TTable - component heb ik nog nooit zien gebruiken ( alhoewel dit natuurlijk perfect kan theoretisch ), maar meestal wordt een combinatie gebruikt van :

    a. Een SELECT-Query: Hierin zit de data van een SELECT, meestal een JOIN over meerdere tabellen.

    b. Een UPDATE-Query: De Update-Query wordt gebruikt om updates te gaan doen, maar enkel en alleen de velden van de eigen tabel, niet van de andere. De Update-Query wordt ook gebruikt om de zogenaamde Mass-Updates te gaan doen op dezelfde tabel. Deze worden geimplementeerd als een procedure binnen het object.

    c. Een Memory-dataset: Dit object wordt ( meestal, niet altijd ) gebruikt als buffer om voorlopige data in op te slaan.
    Last edited by rik.osstyn; 24-Nov-07 at 12:30.
    De verbazing begint waar de kennis ophoudt

  7. #7
    Delphi & OO in Vlaanderen SamWitse's Avatar
    Join Date
    Sep 2007
    Location
    Brussel
    Posts
    833
    De ideeën bestaan inderdaad al een tijdje.
    Het duurt wel een tijdje vooraleer het nut -of juist niet- ervan bewezen wordt.
    Vooral in Java was men daar eerst mee, omdat men daar nou eenmaal geen mooi Delphi-RAD kent, en men wel verplicht was iets nuttigs te maken.

    Jouw constructie van TKlant is inderdaad zoals het hoort. Alleen moet je het nog verder uitbreiden om Data-aware componenten, zoals TDBEdit met de properties (nummer, firmanaam) te kunnen gebruiken.
    Je zult namelijk je eigen TField-afgeleiden moeten maken.

    En als je copleet wil zijn, moet er ook beschreven worden hoe je best associaties, compositions en inherited klassen in je database schrijft.

    Heel belangrijk (voor NLDelphi-lezers) is dat deze werkwijze niet alleen nuttig is in grote bedrijven, maar ook voor je eigen hobby-programma's.

    Sam.
    Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration.

    Sam Witse.
    Delphi & OO in Vlaanderen

  8. #8
    Dat de werkwijze vooral gebruikt wordt in grote bedrijven is geen toeval. Als je een project hebt die veel tabellen aanspreekt ( bijvoorbeeld een paar duizend ), dan is de klassieke Db-RAD-omgeving niet echt meer bruikbaar, ondermeer omdat:

    1. De datamodules zelf ( en de code die er in geschreven zit) zijn niet meer overzichtelijk door het groot aantal componenten.
    2. Automatische Code-generatie is een stuk moeilijker
    3. In grote projecten schrijft iedereen zijn (test-) projekt. Deze code wordt later samengevoegd tot één geheel. Bovenstaande methode leent zich er beter toe.

    Het basis-idee om dergelijke zaken te maken was een noodzakelijke techniek om zowel het onderhoud van de code sterk te verbeteren en de ontwikkeltijd in te korten.

    maar ook voor je eigen hobby-programma's
    Het principe voor hobby-programma's blijft hetzelfde. Persoonlijk gebruik ik ook dezelfde methode om bijvoorbeeld een mp3-speler te maken, maar de gebruikte objecten zijn anders. Bij kleinere projekten gebruikt met eerder een directe afgeleide, zoals TKlant= class(TTable ), bij grote projecten eerder dit zoals beschreven in post 6. het grote voordeel bij kleinere projekten is, dat men zonder veel moeite van techniek kan wisselen ( bijvoorbeeld van TTabel naar Tdbf of van TQuery naar TADOQuery )

    En als je compleet wil zijn, moet er ook beschreven worden hoe je best associaties, compositions en inherited klassen in je database schrijft.
    Wat bedoel je hier juist mee?
    Last edited by rik.osstyn; 28-Nov-07 at 01:08.
    De verbazing begint waar de kennis ophoudt

  9. #9
    Delphi & OO in Vlaanderen SamWitse's Avatar
    Join Date
    Sep 2007
    Location
    Brussel
    Posts
    833
    Als je een klasse
    Code:
    type
    TKlant = class (TTable)
    maakt, dan heb je een een 1-1 mapping tussen een TKlant en een database-tabel met je klanten.
    Als je aan OO-design begint, dan trek je jezelf niets aan van enige database.
    Maar uiteindelijk moet je wel een mapping voorzien met een relationele database.

    Heb je bijvoorbeeld:
    Code:
    type
    TKlant = class (TTable)
    private  
      FAdressenLijst : TList;
    dan kun je die adressen niet in je TTable met klanten kwijt.
    Je zult een 1-n-relatie moeten bouwen met een adressen-tabel in je database.

    Verder, als je ineritance gaat toepassen op TKlant, bijvoorbeeld:
    Code:
    type
    TKlant = class (TTable)  
    :
    end;
    
    TOverheidsKlant = class (TKlant) 
    private
      {speciale fields voor een overheidsklant}
     :
    end;
    ga je dan een aparte tabel maken voor overheidsklanten, ga je een overheidsklant in een klant-tabel steken, en de speciale velden in een aparte tabel, of ga je ALLE klanten in één tabel steken?
    Hiervoor bestaan ook 'standaard' oplossingen.
    Last edited by SamWitse; 24-Nov-07 at 12:59.
    Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration.

    Sam Witse.
    Delphi & OO in Vlaanderen

  10. #10
    Om een antwoord te geven op deze vraag ( en dat zijn in principe een groot aantal vragen in één keer), zal ik beginnen met de principes uit te leggen, later kan ik dan de technieken uitleggen.

    1. Omdat je werkt met data-abstractie, betekent dit dat het ontwerp van je applicatie in principe los staat van je databank en van de Data-componenten ( ADO, BDE, dbExpress, ...). Dit betekent dat je de properties perfect een andere naam kunt geven dan de velden in de databank, maar in de praktijk zal men dit in principe nooit gaan doen.

    Er bestaan een aantal principes om bovenstaande op te lossen, want uiteindelijk moet je Object-structuur de database-structuur volgen en omgekeerd. Hoewel de gebruikte principes in theorie los staan van de gebruikte database-technieken, gaat men dit in de praktijk wel doen en een onderscheid maken tussen Bestands-systemen ( Zoals DBF, Paradox, Access) en de relationele datbanken wegens performantieredenen.

    Om dit te schetsen, kan ik het best doen aan de hand van een voorbeeld met een 1-1 relatie.

    Ik heb een Klant:
    Naam: String;
    VoorNaam: String;
    Adres: String;
    Gemeente: String;
    Bepaalde klanten zijn bedrijven, en daarvoor heb ik een supplementair gegeven nodig:
    BTW-Nummer;
    ga je dan een aparte tabel maken voor overheidsklanten, ga je een overheidsklant in een klant-tabel steken, en de speciale velden in een aparte tabel, of ga je ALLE klanten in één tabel steken?
    Je hebt de redundantie-regels. Deze regels zijn niet alleen geldig voor database-design, maar ook voor algemene zaken. De hoofdregels zijn:
    1. Je mag data maar één keer in je database hebben. Nu daar bestaan er heel wat uitzonderingen op, maar daar ga ik het nu niet over hebben. Je kunt dus de namen niet tweemaal gaan opslaan.
    2. Als je een '1 op n' relatie hebt, of een '1 op 1' relatie met een groot verschil in records, dan maak je twee gelinkte tabellen.

    Volgens deze regels moet je dus twee tabellen maken en deze met een JOIN gaan koppelen.

    Ik verkrijg dus twee tabellen:

    Ik heb een Klant:
    TABEL KLANT
    IdNr: integer, autonummer;
    Naam: String;
    VoorNaam: String;
    Adres: String;
    Gemeente: String;
    En een andere tabel
    TABEL BEDRIJF
    IdNr: integer, autonummer;
    KlantIdNr: integer
    BTW-Nummer: String;
    De relatie kan gebeuren door een aantal technieken.

    1. Een van de meest gebruikte technieken hiervoor is encapsulatie, dit wegens een tweetal redenen:
    - Het werkt ook in geval van een 1-n relatie
    - Het probleem wordt vermeden waarbij dezelfde veldnamen voorkomen in afgeleide classes.

    Code:
    type TBedrijf = class(TdbCustom)
    ...
      property Adres: TKlant read GetKlant;
    ...
      Bedrijf.Adres.Straat:= Edit1.Text;
    Alhoewel het niet tot de theorie behoort, worden in de praktijk in dergelijke objecten toch veel joins gebruikt. De properties die niet behoren door de tabel van de klasse, zijn altijd Read-Only.

    2. Er wordt gebruik gemaakt van hetzelfde object, maar met meerdere constructors of Filter-methods. ( adaptatie )
    Code:
    Klant:= TKlant.CreateKlant( KlantNummer );
    Klant:= TKlant.CreateBedrijf( KlantNummer );
    Afhankelijk van de constructor wordt de selectie gedaan van alle Klanten ofwel een JOIN van de bedrijfklanten met de tabel van de BTW-nummers. Indien het veld niet bestaat ( zoals het BTW-Nummer), dan krijg je een NULL of een lege string terug. Persoonlijk gebruik ik dit zelden of nooit, want het zorgt voor verwarring.

    3. Door afleiding.
    Persoonlijk ken ik deze variant enkel in combinatie met Query's, alhoewel deze met tabellen ook mogelijk is.

    Het principe hiervan is relatief eenvoudig. Beide objecten hebben hun SelectQuery.

    Deze van de klant ziet er zo uit:
    SELECT * FROM TBLKlant
    Als men een hiervan een object gaat afleiden, dan wordt de SelectQuery overschreven en moet men deze query gedeeltelijk herhalen, om er voor te zorgen dat de overgerfde properties nog werken:

    Deze van het bedrijf ziet er zo uit:
    SELECT * FROM TBLKlant, TblBedrijf
    WHERE TblKlant.Idnr = TblBedrijf.Idnr
    Om te schrijven naar de tabellen gaat men op een soortgelijke manier te werken door gebruik te maken van recursiviteit: Zolang de velden niet behoren tot zijn eigen tabel, wordt de gegevens doorgegeven.

    Code:
    procedure WriteField( vFieldname: string; vValue: variant);
    
    begin
      if isMyField( vFieldName )
      then WriteFieldToDb( vFieldName, vValue )
      else inherited WriteField ( vFieldName, vValue );
    end;
    Last edited by rik.osstyn; 24-Nov-07 at 15:13.
    De verbazing begint waar de kennis ophoudt

  11. #11
    En een Object Relational Mapper zorgt voor de mapping van het object naar de relationele database.
    We adore chaos because we like to restore order - M.C. Escher

  12. #12
    Je hebt het hier waarschijnlijk over OPF ?
    De verbazing begint waar de kennis ophoudt

  13. #13
    Een OR Mapper zal waarschijnlijk onderdeel zijn van een Object Persistent Framework, ja
    We adore chaos because we like to restore order - M.C. Escher

  14. #14
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Definieer OR mapper in jouw context Is dat per se dynamisch? Hoe gaat het om met inheritance van objecten?

  15. #15
    Nee, hoeft niet dynamisch te zijn. Hoe die inheritance constructie opgeslagen wordt kan je vaak instellen. Bijvoorbeeld een gehele nieuwe tabel. Of alleen de extra velden in de nieuwe tabel en de rest in de tabel waarvan afgeleid wordt. Of beide in dezelfde tabel.
    We adore chaos because we like to restore order - M.C. Escher

Page 1 of 10 1 2 3 ... LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •