• Nieuwe artikelen

  • Introductie ClientDataSet

    Marcel van Beuzekom



    Een query gebruik je per definitie om gegevens op te halen. Maar na het ophalen wil
    de gebruiker de data ook manipuleren. De gebruiker wil de data in een lijst zien die hij
    snel kan sorteren op verschillende kolommen, en uiteraard wil hij de data ook wijzigen.
    Een query component is hiervoor al  snel te beperkt. Een query is readonly en om de
    data te sorteren moet de query opnieuw worden uitgevoerd, met een trage performance als
    gevolg.



    De BDE en later ook Interbase Express gaven ons al mogelijkheden om met een query
    ook data te wijzigen, maar deze oplossingen zijn geen van allen perfect. Een BDE live
    query is alleen te gebruiken zolang er geen join wordt gebruikt en is bovendien vrij
    traag, BDE cached updates maken tijdelijke Paradox bestanden aan waarin de data wordt
    opgeslagen (in het ergste geval op het netwerk!) en Interbase Express Cached updates biedt
    weer geen algemene oplossing voor alle databases. De ClientDataSet biedt de mogelijkheid
    om data op te halen, sorteren, wijzigen en weer terug op de server op te slaan.



    ClientDataSet



    ClientDataSet is een component, gemaakt voor het bekijken en wijzigen van data in het
    geheugen. Omdat ClientDataSet erft  van TDataSet, heeft het component alle
    mogelijkheden die een gewone dataset ook heeft: bladeren in data, zoeken, data wijzigen en
    alle data-aware componenten gebruiken. Maar ClientDataSet heeft ook verschillende
    mogelijkheden om data te sorteren en te groeperen. Omdat zowel het zoeken als het sorteren
    allemaal in het geheugen gebeurt is het razendsnel. Pas als de wijzigingen weer moeten
    worden opgeslagen hebben we een server of schijf nodig.



    Dat opslaan kan de ClientDataSet zelf, maar in zeer beperkte mate. Het opslaan is
    alleen mogelijk in één bestand en dat bestand wordt ook altijd helemaal opgeslagen .
    Zelfs als je maar één regel hebt gewijzigd wordt dus het hele bestand opgeslagen. Ook
    het inlezen gebeurt weer met het hele bestand. Los van de performance is dit ook niet
    gewenst in een multi-user omgeving, er kan immers maar één gebruiker tegelijk met het
    bestand werken. Toch is ClientDataSet juist voor een multi-user omgeving gemaakt. Voor het
    inlezen en opslaan van de data heeft hij alleen een tweede component nodig: een
    DataSetProvider.



    DataSetprovider



    Een DataSetProvider is eigenlijk niets meer dan een doorgeefluik tussen een
    ClientDataSet en een 'echte' dataset, bijvoorbeeld een Query. De DataSetProvider laat de
    data ophalen door de Query, vat deze data samen in een pakketje en stuurt dat pakketje
    naar de ClientDataSet.







    wpe3.jpg (5097 bytes)

    Inlezen data van een database naar een ClientDataSet



    Het opslaan van de data kan op twee manieren gebeuren: de DataSetProvider kan de data
    zelf in de database opslaan, of hij kan de data terug in de dataset plaatsen. Welke optie
    er wordt gebruikt wordt bepaald door de property ResolveToDataSet van de
    DataSetProvider, staat deze op False dan zal de provider zelf zijn data opslaan, anders
    wordt dat aan de dataset overgelaten. De laatste optie zal het minst gebruikt worden, het
    probleem was tenslotte dat die Query readonly was.




    wpe4.jpg (4137 bytes)

    Opslaan data van een ClientDataSet naar een Database




    Data opslaan



    Het opslaan van de data doet de provider dus meestal zelf. Het doet dat door het
    genereren van een SQL  statement in de vorm:



    update <TableName> 
    set <FieldX> = <ValueX>
    where <KeyX> = <KeyValueX>


    Om dit statement uit te voeren heeft de provider dus de volgende gegevens nodig:



    update <TableName>  



    Tabel waarin de wijzigingen worden opgeslagen. Bij een eenvoudige query vanuit
    één tabel kan de provider de tabel zelf achterhalen, bij een uitgebreidere query moet
    deze worden ingevuld in het OnGetTableName event. Let op dat je dus
    altijd maar in één tabel kunt opslaan.




    set <FieldX> = <ValueX>  



    De gewijzigde velden en hun nieuwe waarden  Deze data wordt door de ClientDataSet
    aan de provider gegeven




    where <KeyX> = <KeyValueX>  



    Het statement dat bepaalt welke regel er moet worden gewijzigd. De manier waarop dit
    gedeelte wordt aangemaakt wordt bepaald door de UpdateMode property van de provider:



    • upWhereAll

      Alle velden van de query worden gebruikt in het where-statement. Als een andere gebruiker
      dus inmiddels 'achter onze rug' de data heeft gewijzigd zal de provider geen enkele regel
      wijzigen

    • upWhereChanged

      Alleen de gewijzigde velden worden gebruikt in het where-statement. Als een andere
      gebruiker dus inmiddels 'achter onze rug' dezelfde velden heeft gewijzigd zal de
      provider geen enkele regel wijzigen. Heeft de andere gebruiker wel dezelfde regel(s)
      gewijzigd, maar niet dezelfde velden dan wordt de wijziging wel doorgevoerd

    • upWhereKeyOnly

      Alleen de primary key velden worden gebruikt in het where-statement. Welke velden dit zijn
      wordt bepaald door de query waarmee de data wordt opgehaald. Alle velden waar pfInKey in
      de provideroptions aanstaat worden gebruikt. Als een andere gebruiker dus inmiddels
      'achter onze rug' de data heeft gewijzigd zal de provider deze wijzigingen overschrijven




    Als de combinatie van de properties niet juist is gezet zal het opslaan van de data
    niet lukken, de provider zal één van de volgende foutmeldingen geven:





    • Update affected more than 1 record

      Het gegenereerde statement leidde niet tot een unieke regel. Dit kan zijn omdat de primary
      key  niet in het SQL-statement is meegenomen of omdat UpWhereChanged wordt gebruikt
      en de gewijzigde gegevens niet uniek zijn. Een wijziging van de naam 'Jansen' in 'Janssen'
      zal dan (bijna) altijd een foutmelding geven, er waren immers meerdere regels met de naam
      'Jansen' en de provider kan niet vinden om welke regel het gaat.

    • Unable to find record. No key specified

      Het gegenereerde statement leidde niet tot het vinden van een regel. Meestal zal dat komen
      omdat er geen velden in de query zijn waar de provideroptions een pfInKey bevat.




    Testprogramma



    Het testprogramma laat alle stappen zien die benodigd zijn om data te kunnen ophalen,
    sorteren, wijzigen en wegschrijven met behulp van een ClientDataSet.




    • Plaats een TQuery component en verwijs de database naar DBDemo's

    • Vul het volgende SQL-statement in:
      SELECT Orders.OrderNo, Orders.CustNo, Customer.Company 
      Orders."Customer name", Orders.SaleDate, Orders.ShipDate,
      Orders.EmpNo, Employee.LastName Orders."Employer LastName",
      Orders.ShipToContact, Orders.ShipToAddr1, Orders.ShipToAddr2,
      Orders.ShipToCity, Orders.ShipToState, Orders.ShipToZip,
      Orders.ShipToCountry, Orders.ShipToPhone, Orders.ShipVIA,
      Orders.PO, Orders.Terms, Orders.PaymentMethod, Orders.ItemsTotal,
      Orders.TaxRate, Orders.Freight, Orders.AmountPaid
      FROM "orders.db" Orders
      INNER JOIN "customer.db" Customer ON (Orders.CustNo = Customer.CustNo)
      INNER JOIN "employee.db" Employee ON (Orders.EmpNo = Employee.EmpNo)


    • Ga naar de fieldseditor en voeg alle velden toe (CTRL-A)

    • Zet bij de velden CustomerName en EmployerLastname ReadOnly
      op True.

      Deze velden komen uit een andere tabel en mogen dus niet via deze query worden gewijzigd

    • Zet bij het veld OrderNo, property ProviderOptions de pfInKey
      op True

      Dit is de primary key van de query, deze zal worden gebruikt om de juiste regel terug te
      vinden bij het opslaan

    • Plaats een TDataSetProvider component en vul bij de dataset de zojuist
      aangemaakte query in

    • Zet de UpdateMode van de provider op upWhereKeyOnly

    • Plaats een TClientDataSet component en vuld bij de providername
      de naam van de zojuist aangemaakte provider in

    • Maak een OnReconcileError-event aan voor de clientdataset en zet hier
      de volgende regel:

    • ShowMessage(E.Message);


    • Plaats een TDataSource en vul bij de dataset de naam van de zojuist
      aangemaakte ClientDataSet in

    • Plaats een TDataGrid en vul bij de datasource de  naam van de
      zojuist aangemaakte datasource in

    • Maak een OnTitleClick event aan en vul hier de volgende
      regel  in:

    • ClientDataSet.IndexFieldNames := Column.FieldName;

    • Maak een OnCreate event aan voor het formulier en zet hier de volgende
      source:

    • ClientDataSet.Open;

    • Plaats een TButton, vul bij de caption 'Opslaan' en en in het
      OnClick-event de volgende source:

    • if ClientDataSet.ApplyUpdates(0) <> 0 then
      ShowMessage('Oops')
      else
      ClientDataSet.Refresh;



    Compileer en start het testprogramma. In de DBGrid kunnen alle gegevens worden
    gewijzigd, behalve de velden die we op ReadOnly hadden gezet. Als er op de knop 'Opslaan'
    wordt gedrukt worden alle wijzigingen opgeslagen en ververst. Als er op een titel van een
    kolom wordt gedrukt wordt de data gesorteerd op deze kolom. We hebben nu dus data uit een
    database die we kunnen sorteren zonder verlies van performance en wijzigen zonder gebruik
    van live queries of cached updates. Niet slecht voor een eerste les in
    ClientDataSets!



    Het vervolg



    In volgende artikelen zal ik verder ingaan op de ApplyUpdates en de gevolgen hiervan.
    Ik zal meer uitleggen over het genereren van foutmeldingen, uitvoeren van validaties en
    het opvangen van foutmeldingen. Ook zullen er nog uitgebreidere sorteermogelijkheden aan
    bod komen. Uiteindelijk zullen we uitkomen bij het ultime gebruik van ClientDataSet: een
    n-tier applicatie met gebruik van MIDAS.