A Delphi programozási nyelv

Adatbáziskezelés

Bevezetés

    Az adatbázis-kezelés a Delphi környezet egyik kulcsfontosságú eleme. Az ide kapcsolódó információ mennyiségének tekintetében, többkötetes könyvet lehetne írni (mint ahogy természetesen léteznek is ilyen könyvek), ami kizárólag ezzel a témával foglalkozik. Eme rész megírásakor nem az volt a cél, hogy egy-egy témát csak néhány mondat erejéig érintve, felvázoljuk a Delphi teljes adatbázis kezelését, és nem is az volt a cél, hogy a teljes témából egy részt kiragadva lemenjünk a legapróbb részletekig. Ehelyett inkább egy általános tájékoztatás és egy irányvonal meghatározása volt a cél. Viszont ha ez kiegészítésre kerül az adatbázisokkal kapcsolatos általános ismeretekkel (mi az adatbázis, mi az SQL, stb.) illetve az egyes komponensekhez tartozó leírással, amit a Delphi súgója tartalmaz, akkor az már elegendő lehet egyszerűbb gyakorlati alkalmazások megírására.

A feladat

    Ha a Delphi szemszögéből megvizsgáljuk az adatbázis kezelés fogalmát, akkor szinte magától adódik, hogy ez a komplex feladat néhány egymástól jól elválasztható részfeladatra bontható. Így pl. biztos, hogy kell egy olyan réteg, ami az adatbázissal való fizikai kapcsolatot valósítja meg. Aztán a már elérhető adatbázisból valahogyan adatokat kell szolgáltatni. Pontosabban nem is csak szolgáltatni, hiszen egy programnak lehet egyaránt olvasási és írási szándéka is, hanem a teljes kétirányú adatáramlást vezérelni. Ha pedig az adatok már rendelkezésre állnak, akkor azokat valahogyan meg kell jeleníteni, vagy szaknyelven mondva, biztosítani a felhasználó számára az interfészt. Mindegyik ilyen részfeladat megoldására, a programozó részére több komponenscsalád áll rendelkezésre. Egy komponenscsalád általában egy adott részfeladat megoldásában nyújt segítséget.

Amint ismeretes, manapság nagyon sokféle adatbázis létezik. És bár ma már szinte mindegyik SQL alapú, azért a kezelésükben igen komoly eltérések is vannak. Egyrészt magának az SQL-nek is vannak különböző dialektusai. Hogyha ettől el is tekintünk, akkor is szembesülünk azzal, hogy különböző adatbázisok különböző típusokat, indexelési módszereket, stb. támogatnak. Ez nyílván probléma, aminek megoldása nem egy egyszerű feladat. De ha jól belegondolunk a fent említett részfeladatokba, akkor azért megoldható, hogy az adatbázis-specifikusság csak egy bizonyos szintig álljon fenn. Így pl. ha kellő átgondoltsággal írjuk meg (és a Delphi fejlesztői nyílván így is tettek) az adatokat megjelenítő komponenseket, akkor megoldható az, hogy ott már ne legyen jelentősége annak, hogy az adatok milyen típusú adatbázisból származnak. Mint ahogy azt később látni fogjuk, a Delphi még ennél is rugalmasabb. Egyrészt engedélyezi közvetlen SQL utasítások programozását, lehetővé téve ezzel a programozónak, hogy kihasználhatja egy adatbázis specifikus lehetőségeit. De hogyha ehhez nem ragaszkodunk, akkor a teljes adatáramlás vezérlését (második részfeladat) a Delphi leveszi a programozó válláról. Így szinte teljesen adatbázis-független alkalmazásokat készíthetünk.

Megjegyezzük, hogy a fent leírtak egy egyszerű adatbázisos alkalmazásra vonatkoztak. Külön témát képviselnek az úgynevezett többrétegű adatbázisos alkalmazások. Ezeknek az alkalmazásoknak a lényege az, hogy adott egy adatbázisszerver, ami az adatok fizikai tárolásáért felelős, adott egy köztes réteg, amely az alkalmazás fő logikáját tartalmazza és adott a kliens réteg, aminek több példánya is lehet és ami a felhasználói felületet biztosítja. A kliens réteg a középső réteggel áll kapcsolatban, az pedig az adatbázis szerverrel. A Delphi fejlett támogatást nyújt az ilyen alkalmazások megírására (DataSnap) de ezzel itt most nem foglalkozunk.

A legalsóbb réteg (még mielőtt a komponensekről beszélnénk)

    Ahogyan fentebb említésre került, az adatbázis-kezelés egyik részfeladata az adatbázisokkal való fizikai kapcsolat létrehozása. A Delphi erre több lehetőséget is kínál.

A BDE (Borland Database Engine)
A BDE egy univerzális adatbázisokat kezelő motor, ami már a Delphi első verziójában is megtalálható volt. A BDE több típusú SQL és nem SQL alapú adatbázist is támogat. A lényeg az, hogy a programozó a programból egy egyedi névvel hivatkozik az adatbázisra. A BDE-t külsőleg kell bekonfigurálni (BDE administrator) arra vonatkozóan, hogy az adott egyedi nevű adatbázis milyen típusú és hol található. Így az alkalmazás leválasztásra kerül a használt adatbázistól, de ha a programozó úgy kívánja a BDE lehetővé teszi közvetlen, specifikus parancsok végrehajtását is, de ezzel már nyílván sérül az alkalmazás adatbázistól való függetlensége. Ezen kívül, a BDE olyan magas szintű tulajdonságokkal rendelkezik, mint pl. a gyorsító-tárazás, vagy heterogén táblaegyesítés.
Az ADO (ActiveX Data Objects)
Az ADO a Microsoft magas szintű felülete. Ennek működése az OLE DB adatelérési technológián alapul, amely elérést biztosít relációs és nem relációs adatbázisokhoz, levelezési és fájlrendszerekhez, stb.
A dbExpress
A dbExpress a Borlad egyik újabb adatbázis kezelő rétege volt, amely a Delphi 6-ban jelent meg. Két legfontosabb jellemzője a kis méret és hordozhatóság. A többi "erőművel" összevetve a dbExpress képességei erősen korlátozottak: csak SQL kiszolgálókat képes elérni, nem tud gyorstárat használni, csak egyirányú adathozzáférést tesz lehetővé, stb. Első ránézésre ez visszalépésnek tűnik. A korábbi adatbázismotorok által natívan végzett műveletek itt komponens szintre kerültek át. Csakhogy ezek a komponensek sokkal nagyobb mértékű irányítást tesznek lehetővé a dbExpress alapú alkalmazás számára mint egy önálló, monolitikus adatbázismotor, amely végrehajt helyettünk bizonyos műveleteket, de gyakran nem úgy, ahogy azt mi szeretnénk, hanem ahogy azt ő szeretné.
Az InterBase Express
Az univerzális adatbázismotorokon kívül, szinte mindegyik adatbázishoz léteznek natív komponenscsaládok, amelyek kizárólag az adott típusú adatbázisok kezelésére alkalmasak, viszont általában hatékonyabbak mint az univerzális adatbázis kezelők. Az InterBase a Borland saját fejlesztésű SQL alapú adatbázisa. Ezt az adatbázist natívan kezelő komponenscsalád alapból bent van a Delphi-ben. Ez az InterBase Express, vagy rövidítve IBX.
A FireDAC
A FireDAC a Delphi fejlesztőjének (Embarcadero) legújabb adatbázismotorja, amit a dbExpress utódjának szántak. Érdekes, hogy a BDE-vel és a dbExpress-sel is felfedezhető bizonyos hasonlóság. A dbExpress-hez képest jóval több adatbázist támogat. Ezen kívül, komponens szinten, tartalmaz olyan elemeket mint a teljesítménymonitor (ADMonitor), adatbázis böngésző (ADExplorer) valamint konvertáló eszközöket is kínál, amelyek BDE-t használó alkalmazások egyszerű átalakítását teszik lehetővé.

A DataSet komponens

    Kicsit egyszerűsítve azt lehetne mondani, hogy a TDataSet osztály gyakorlatilag egy adatbázis táblát reprezentál. Mint ismeretes, egy táblában vannak mezők és sorok. Egyszerre csak egy sor lehet aktív. Csak az aktív sor mezőinek értéke érhető el. Csak az aktív sort lehet módosítani, stb. A DataSet komponens egy eléggé összetett komponens, ezért itt csak a főbb jellemzőivel foglalkozunk. Ezen kívül fontos megemlíteni, hogy a TDataSet egy absztrakt osztály, így közvetlenül nem használható. Viszont valamennyi adatbázis adatokat szolgáltató komponens a TDataSet leszármazottja.

A sorokat illetően a TDataSet osztály főbb jellemzői és metódusai az alábbiak.
Jellemzők:

RecordCount
visszaadja a sorok számát
RecNo
visszaadja az aktuális rekord számát
Bof
igaz értéket add vissza, ha az első sor az aktív
Eof
igaz értéket add vissza, ha az utolsó sor az aktív
Metódusok:
First
aktívvá teszi az első sort
Last
aktívvá teszi az utolsó sort
Prior
aktívvá teszi az előző sort
Next
aktívvá teszi a következő sort
MoveBy
előre vagy hátra "lép" a paraméterként megadott számú sort
Az oszlopokat a TDataSet osztályban a Fields jellemző határozza meg, ami egy TField típusú objektumokból álló lista. A TField osztály egy mezőt határoz meg. A TField osztály is eléggé összetett és egy adatbázistábla mezejéről szinte minden információ megtalálható benne. Íme néhány a főbb jellemzők közül:
AsBoolean
visszaadja az aktív sor e mezőjének értékét Boolean típusként
AsInteger
visszaadja az aktív sor e mezőjének értékét Integer típusként
AsFloat
visszaadja az aktív sor e mezőjének értékét Real típusként
AsString
visszaadja az aktív sor e mezőjének értékét String típusként
DataType
visszaadja a mező típusát
DataSize
visszaadja a mező egy elemének tárolására szükséges memóriaterület hosszát
FieldName
visszaadja az mező nevét az adatbázisban
Text
visszaadja a mező megjelenítési nevét
FieldNo
visszaadja a mező számát
IsNull
igaz értékkel tér vissza, ha a mező "Null" értéket tartalmaz
A TDataSet osztályban egy konkrét mezőt általában az alábbi jellemzőkkel, illetve metódusokkal érhetünk el.
Jellemzők: Metódusok: Így pl. ha az aktív sor második celláját szeretnénk visszakapni egész számként, akkor valami hasonlót kell írnunk:
Var a: Integrer; ... a := DataSet.Field[1].AsInteger;
Ha pedig pl. az aktív sor UserName nevű mező értékét szeretnénk visszakapni szövegként, akkor azt így érhetjük el:
Var s: String; ... s := DataSet.FieldByName('UserName').AsString;
Egy DataSet által reprezentált adathalmazt csak akkor lehet módosítani, ha meghívtuk a DataSet edit metódusát. Nyilván, csak az aktív rekord adatai módosíthatóak. Új, üres sor beszúrása az Insert metódus meghívásával történik. A szerkesztés lezárása pedig a Post metódus meghívásával történik.

Adatbázis adatokat szolgáltató komponensek

    Ahogyan azt korábban említettük, a Delphi az adatbázis-eléréshez többféle eszközt kínál: BDE, dbExpress, stb. A programozó számára, mindegyik motort egy-egy komponenscsalád reprezentálja. Ezeket az alábbi táblázat foglalja össze:

Adatbázis motor Komponenscsalád
BDE BDE
ADO dbGo
dbExpress dbExpress
Interbase Express Interbase
FireDAC FireDAC
Nem fogjuk egyenként áttekinteni valamennyi család legalább főbb komponenseit, de mivel mindegyik család feladata azonos, így több családban is megtalálhatjuk az ugyanazt a funkciót végző komponenst, csak éppen más környezetben. Így, funkcionalitásuk szerint csoportosítva, nézzük át röviden a különböző komponenscsaládokban található főbb komponenseket. Mivel különböző családokban az azonos feladatot ellátó komponensek jellemzői igen eltérőek lehetnek, így azokat név szerint nem említjük. Ehelyett csak egy általános leírást adunk a komponensről. Egy konkrét komponens jellemzőinek leírása a Delphi súgójából is kiolvasható.
Az adatbázis komponens
Szinte mindegyik komponenscsaládban található egy az adatbázissal való kapcsolatot reprezentáló komponens. Különböző családokban az adott komponens nevét az alábbi táblázat mutatja:
Komponenscsalád Komponens neve
BDE TDatabase
ADO TADOConnection
dbExpress TSQLConnection
Interbase TIBDatabase
FireDAC TADConnection
Valamennyi komponens a TCustomConnection osztályból származik. Ezekben a komponensekben lehet megadni az adatbázis elérését (útvonal vagy alias), felhasználói nevet, jelszavat és egyéb más paramétereket. A csatlakozást a Connected jellemző Igaz értékre állításával lehet kezdeményezni. A kapcsolat létrejöttéről az AfterConnection eseményből értesülhetünk.
Tábla komponensek
Szinte mindegyik komponenscsaládban található egy az adatbázis tábláját reprezentáló komponens. Különböző családokban az adott komponens nevét az alábbi táblázat mutatja:
Komponenscsalád Komponens neve
BDE TTable
ADO TADOTable
dbExpress TSQLTable
Interbase TIBTable
FireDAC TADTableAdapter
Valamennyi komponens a TDataSet osztályból származik, de nem közvetlenül. Ezekben a komponensekben általában meg lehet adni közvetlenül egy adatbázist, vagy egy adatbázis komponenst, és az adott adatbázis egyik táblájának nevét. Az adatok beolvasása az Active jellemző Igaz értékre való állításával történik. Ha a Delphi integrált fejlesztői környezetében rákattintunk egy DataSet komponensre (pontosabban annak egyik utódjára), akkor előjön a mezőszerkesztő. Itt megadhatjuk, hogy lekérdezéskor milyen mezők kerüljenek a listába. Ha egy mezőt sem adunk meg, akkor a komponens ezt automata állapotnak veszi, és ekkor beolvasásra kerül az adatbázis mindegyik mezeje. A tábla komponensben szűrőt is be lehet állítani. Így csak bizonyos kritériumnak megfelelő rekordok kerülnek beolvasásra. Egy fizikai adatbázis adatforgalma (rendszerint SQL parancsok), a tábla komponensek használata esetén rejtve marad a programozó előtt.
SQL parancs komponensek
Mindegyik komponenscsaládban található egy SQL utasítást reprezentáló komponens. Különböző családokban az adott komponens nevét az alábbi táblázat mutatja:
Komponenscsalád Komponens neve
BDE TQuery
ADO TADOQuery
dbExpress TSQLQuery
Interbase TIBQuery
FireDAC TADQuery
Valamennyi komponens a TDataSet osztályból származik, de nem közvetlenül. Ezekben a komponensekben lehetőség van egy tetszőleges SQL parancs megadására. Ha ez a parancs rendelkezik visszatérési értékkel (kizárólag a Select utasítás), akkor annak végrehajtását az Open metódus meghívásával kezdeményezzük. Ezt követően, miután befejeztük az adathalmazzal való munkát, meg kell hívni a Close metódust. Ha a megadott SQL parancs nem rendelkezik visszatérő értékel, akkor annak végrehajtását az ExecSQL metódus meghívásával kezdeményezzük.
Az előbbiekhez hasonlóan, mindegyik komponens család tartalmaz tárolt eljárást reprezentáló komponenst (*StoredProc), tranzakció kezelő komponenst (*Transaction), és még több mást is, de ezekkel itt most nem foglalkozunk.

A kakukktojás: a TClientDataSet komponens

    A TClientDataSet komponens ugyancsak nem közvetlenül, de a TDataSet osztályból származik. Tehát egy adatbázis táblájaként viselkedik, de a fent említett komponensektől eltérően, fizikai adatbázishoz nem csatlakozik. Ehelyett minden adatot a memóriában tárol. Természetesen ez esetben mindig nekünk kell megszerkeszteni az adathalmaz struktúráját (milyen mezőkből áll össze). Viszont lehetőség van a teljes adathalmaz (a struktúra szerkezetével együtt) kiírására egy fájlba vagy stream-be. Ez a SaveToFile vagy SaveToStream metódusok meghívásával történik. Ráadásul paraméterként megadható, hogy a fájl vagy stream felépítése bináris legyen vagy XML. Ha egyszer létezik fájlba és stream-be kiíró parancs, akkor nyilván létezik ezekből beolvasó parancs is. Ezek a LoadFromFile és a LoadFromStream metódusok.

A TClientDataSet-et akkor használjuk, ha emulálni szeretnénk egy adatbázist. Pl. ha táblázatos adatokkal szeretnénk dolgozni, majd át szeretnénk azt küldeni egy távoli végpontnak, akkor az egyik kényelmesnek tűnő megoldás az lehet, hogy használva a TClientDataSet-et, hagyományos adatbázisként kezeljük az adatokat (lásd lejjebb) majd az adatállományt elmentjük egy stream-be, amit hálózati kapcsolaton átküldhetünk egy távoli végpontra.

A DataAccess komponenscsalád

    Ebből a családból most csak két komponenst említenék meg. Az egyik a TClientDataSet, amiről az imént eset szó. A másik a TDataSource komponens. A TDataSource komponens nem más mint egy köztes réteg az adatokat szolgáltató és adatokat megjelenítő komponensek között. Így hát valamennyi adatbázisból származó adatot megjelenítő komponensnek adatforrásként a DataSource jellemzőben egy TDataSource típusú objektumot kell megadni, aminek aztán a DataSet jellemzőjében pedig már egy TDataSet-et nyújtó komponenst.

Megjelenítés: a DataControl komponenscsalád

    Az adatbázisból származó adatok megjelenítésére a DataControl család komponensei hivatottak. Szinte mindegyik szabványos Windows kontrollnak (címke, szövegdoboz, checkbox, stb.) megtalálható itt a megfelelője. A különbség csak annyi, hogy míg az előbbiek változókkal dolgoznak, ezek a komponensek az adatbázis adataival. Valamennyi ilyen komponens rendelkezik egy DataSource jellemzővel, amelyben egy TDataSource típusú objektumot kell feltünteti (lásd feljebb). Ha az adatbázissal a kapcsolat aktív, akkor a komponens már fejlesztési időben is működhet. Az alábbi listában néhány adatmegjelenítő komponens megnevezése és rövid leírása látható:

TDBGrid
Egy táblázatot jelenít meg
TDBNavigator
Egy nyomógomb kollekció, aminek segítségével lépkedhetünk a sorok között, kiadhatjuk a szerkesztés vagy sorbeszúrás parancsot, stb.
TDBText
A címkének felel meg. Megjeleníti egy mező értékét nem módosítható módon.
TDBEdit
A szerkesztő doboznak felel meg. Megjeleníti egy mező értékét lehetőséget biztosítva annak módosítására.
TDBListBox
A ListBox-nak felel meg. Előre meghatározott elemek közüli választást tesz lehetővé.
TDBComboBox
A ComboBox-nak felel meg. Használható zárt kijelöléshez és felhasználó általi adatbevitelhez is.
Természetesen a család ezeken kívül még számos komponenst tartalmaz. Ahogyan a fentieknél is, sok esetben már a nevükből megállapítható, hogy az adott komponens mire szolgál. Általában működésük nem bonyolult, és a Delphi súgója elegendő információt nyújt azok megértéséhez.

Zárszó

    A fent leírtak csak egy ízelítőt adtak a Delphi adatbázis-kezeléséről. Így pl. nem esett szó a többrétegű adatbázisos alkalmazások készítéséről, a saját adatbázis-komponensek megírásának lehetőségéről és még sok másról. A Delphi kezdettől fogva erős támogatást nyújtott az adatbázisos alkalmazások fejlesztésére, mert fejlesztői felismerték, hogy nagyon sok esetben pont erre van szükség. Ha ilyen feladat előtt állunk, a Delphi mindenképpen jó választásnak tűnik.