Data module met universele procedure om record toe te voegen

Gambas Modules of minstens stukken ervan kan je zo universeel maken dat je ze gemakkelijk kan hergebruiken.
Bij gebruik van een databank (bv MySQL/MariaDB) moet je altijd code schrijven om een record aan te maken. Het gemeenschappelijke daarin is minstens het toevoegen van een record met een sleutel (record id).

Je kan de aanmaak van een nieuw record (dus met nieuwe gegevens) opsplitsen in twee delen:

  • Aanmaken van een leeg record met een nieuw bekomen record nummer.
  • Het aanvullen van dat nieuwe record met alle andere gegevens, een update dus.

De update procedure moet je waarschijnlijk toch ook maken om gegevens te kunnen wijzigen aan een record waarvan je de id kent.

Het aanroepend programma kan gewoon een “ModData.saveRecord(id, data)” gebruiken, de module splitst het op in twee delen.
Als de id gegevens is, wordt de update procedure aangeroepen.
Als de id = 0 moet er een nieuw record gemaakt worden, daarna de update procudure met de ondertussen verkregen id.

Een datamodule ModData bevat:

PRIVATE sLastError AS String
' ...
PUBLIC SUB addRecord(sTablename AS String, sKeyname AS String) AS Long
' adds a record to a given database, get the record id of the empty record 
  DIM sSql AS String  
  DIM myResult AS Result
 ' 
  sSql = "INSERT INTO " & db.Quote(sTablename) & " (" & sKeyname & ") VALUES(0)" 
  DEBUG sSql
  '
  TRY $hConData.Exec(sSql)
  IF ERROR 
    sLastError &= "insert; " & Error.Text
    RETURN 0
  ELSE 
    sSql = "SELECT LAST_INSERT_ID() AS myId "
    'myId" ' FROM " & db.Quote(sTablename)
    TRY myResult = $hConData.Exec(sSql)
    IF ERROR 
      sLastError &= "last id ?" & Error.Text
      RETURN 0
    ELSE 
      RETURN myResult!myId 
    ENDIF 
  ENDIF 
  DEBUG sLastError
END

De procedure geeft het aangemaakte recordnummer terug. Daarna kunnen de gegevens ingevuld worden, wat minder universeel is. Veel kans dat dit stuk code enkel intern vanuit de module aangeroepen wordt, je zou ze dus ook PRIVATE kunnen maken.
Fouten vraagt het aanroepend programma/aanroepende procedure op met getLastError(), wat geimplementeerd is als

PUBLIC SUB getLastError() AS String
  '
  RETURN sLastError
  '
END

Alle procedures in de datamodule die een record moeten toevoegen in een database, kunnen deze zelfde functie gebruiken, mits aangeroepen met de juiste parameters voor de betreffende tabel.

Voorbeeld:

# Name Type Null Default Extra
1 emp_id bigint(20) No None AUTO_INCREMENT
2 emp_data1 int(11) No 0  
3 emp_data2 int(11) No 0  
4 emp_data3 char(1) Yes NULL  
5 emp_data4 varchar(11) Yes NULL  
6 emp_data5 datetime Yes NULL  
7 emp_data6 datetime Yes NULL  
8 emp_cre timestamp Yes CURRENT_TIMESTAMP  
9 emp_creby char(24) Yes NULL  
10 emp_upd datetime Yes NULL  
11 emp_updby char(24) Yes NULL  

Dit werkt op voorwaarde dat

  • de key record_id een getal is, bv van het type bigint(20)
  • de waarde van het veld record_id altijd automatisch wordt toegekend door de database (Auto Increment)
  • er standaardwaarden voorzien zijn in de struktuur van de tabel (0, NULL, ..).

Als geen standaardwaarden voorzien zijn krijg je een foutmelding; de database krijgt enkel een opdracht om de key in te vullen en weet niet wat er met de andere velden moet gebeuren.

Andere dingen die je kan standaardiseren:

  • Een foutmelding bijhouden (zie ook hierboven sLastError) en op laten vragen.
  • Een record verwijderen dat een bepaalde keywaarde heeft.
  • Opbouwen, openen en sluiten van de databaseverbinding
  • Een keuze tussen verschillende datasets voorzien (bv laptop/netwerk, ontwikkeling/productie)
  • enz.

Metadata
Dikwijls is het nuttig om te weten wanneer een record ontstaan is (creation date-time), en wie het gemaakt heeft (user). Als je daarvoor standaard velden opneemt in de database kan je die laten invullen vanuit deze record toevoegen-procedure. Het eenvoudigste voor het “createdon” veld is in de database als standaardwaarde “CURRENT_TIMESTAMP” in te stellen. Dan blijft nog het “createdby” veld, waar we de gebruikerslogin naam kunnen invullen (gb.user.name). Ofwel laat je een waarde opslaan die als parameter van de oproepende procedure komt, en kan het een andere vorm van gebruikers-id zijn, bv die waarmee in de applicatie ingelogd wordt.

  • Database: employee
  • Velden: id, naam, address, …, createdon, createdby
  • Database: department
  • Velden: id, building, floor, …, createdon, createdby

Dit is universeel; je kan employee.createdby of department.createdby enz. gebruiken; bij de AddRecord is het steeds dezelfde naam. Je moet dat dan voor alle tabellen zo doen!

Een andere mogelijkheid is de createdby optioneel te maken, zodat je ook nog minimale tabellen met enkel id kan houden.
En: als je niet de standaardnaam gebruikt, maar velden met een prefix voor de tabel gebruikt, moet je de veldnaam ergens vandaan halen.

  • Database: employee
  • Velden: emp_id, emp_naam, emp_address, …, emp_createdon, emp_createdby
  • Database: department
  • Velden: dep_id, dep_building, dep_floor, …, dep_createdon, dep_createdby

Gecombineerd:

PUBLIC SUB addRecord(sTablename AS String, sKeyname AS String, OPTIONAL sCreatedByField AS String) AS Long 
' adds a record to a given database, get the record id of the empty record 
  DIM sSql AS String  
  DIM myResult AS Result
 '
  sSql = "INSERT INTO " & db.Quote(sTablename) & " (" & sKeyname 
  IF sCreatedByField
    sSql &= "," & sCreatedByField & ") VALUES(0,&1)" ' & db.Quote(User.Name) & ")"
  ELSE 
    sSql &= ") VALUE(0)" 
  ENDIF 
  '
  DEBUG sSql
  '
  TRY $hconData.Exec(sSql, User.Name)
  IF ERROR 
    sLastError &= "insert; " & Error.Text
    RETURN 0
  ELSE 
    sSql = "SELECT LAST_INSERT_ID() AS myId "
    '
    TRY myResult = $hConData.Exec(sSql)
    IF ERROR 
      sLastError &= "last id ?" & Error.Text
      RETURN 0
    ELSE 
      RETURN myResult!myId 
    ENDIF 
  ENDIF 
  DEBUG sLastError
END 

De procedure AddRecord krijgt een optionele parameter “sCreatedByField”.

Als die ingevuld is, wordt dat veld gebruikt om de gebruikersnaam in te bewaren; ‘User.Name’ kan je in Gambas altijd gebruiken en geeft de loginnaam van de gebruiker die het programma draait. Hier wordt die ingevuld vanuit AddRecord, zodat er van het oproepend programma niet aan gedacht moet worden, of ook de waarde niet vergeten of verkeerd ingevuld kan worden; zelfs als de update nadien niet werkt heb je al meta-informatie.

Als geen veldnaam wordt doorgegeven om de createdby informatie in op te slaan, wordt enkel de record_id ingevuld.

Upd: Ps: misschien gebruik ik ook beter geen tabelnaam-PREFIX in de veldnamen … (http://gambas.copyleft.be/blog/archives/1453)

This entry was posted in Databank, Gambas2. Bookmark the permalink.