Adatbáziskezelés Delphiben – II. Rész: Mélyedjünk el az adatok kezelésében…
A cikksorozat második része tovább vezeti
az olvasót a Delphi adatbáziskezelésben
való elmélyülésben.
A TDataModule osztály
Ennek az osztálynak a
szerepe, hogy olyan komponenseket tároljon, amelyek csak tervezési időben
láthatók. Ilyen a TTimer,
TTable, TDataSource és még sorolhatnánk. Ennek a jelentősége az,
hogy az ilyen komponenseket elegendő egyszer elhelyezni az adatmodulban és
onnan mindegyik form képes azt elérni. Ezen felül még olyan eljárásokat is megírhatunk itt,
amelyeket azután minden unitban elérhetünk.
Adatmodult készíteni
borzasztó egyszerű és tulajdonképpen két lehetőségünk is van rá. Az
egyik, hogy használjuk a File
menü New Data Module menüpontját,
a másik pedig a File
menü New parancsa, amely hatására megjelenő párbeszédablakban kiválasztjuk
a Data Modules lapot és a ott található, előre elkészített adatmodulok közül
kiválasztjuk azt, amelyik számunkra szükséges. Bármelyik megoldást is
választjuk, a művelettel megjelenik egy fehér munkafelületű ablak,
amelybe elhelyezhetjük a különböző vezérlőelemeket.
Az osztálynak két,
gyakran használt eseménye van, az OnCreate és az OnDestroy. Az első akkor hajtódik végre, amikor létrehozza
a program az adatmodult. Itt szokás táblákat megnyitni, valamint egyéb más
olyan műveleteket elvégezni, amelyeket a létrehozáskor végre kell hajtani.
Az OnDestroy a modul bezárásakor hajtódik végre, éppen ezért itt
például bezárhatjuk a táblákat.
A DataMosule-nak
csupán két tulajdonsága van, de számunkra most csak egy fontos, mégpedig az
adatmodul neve (Name).
Ezt mindig változtassuk meg olyan névre, amelyről azonnal fel tudjuk
ismerni, hogy a programunk mely alkotórészéről van szó.
Amikor az adatmodulban
található komponensekhez szeretnénk hozzáférni programból, akkor tudatni kell a
unittal, hogy hol találja azokat. Éppen ezért a unit Implementation részében meg kell adni az adatmodul unitjának a
nevét, majd a CTRL+F9 kombinációval le kell fordítani a programot, hogy
ezek a változások érvényre jussanak. A modul megadása az alábbi módon történik:
implementation
uses UDM; //ahol az UDM
az adatmodul unitjának a neve.
Ezt
követően az adatmodulban található komponensekre az alábbi módon
hivatkozhatunk:
adatmodul_neve.komponens_neve
DM.tblAllatok //ahol a DM az
adatmodul, a tblAllatok pedig a rajta található tábla
neve
Gyakorlás…
Készítsük el az
előző rész végén ismertetett alkalmazást, de most a Table és a DataSource komponenseket tegyük egy adatmodulra.
Adatok kezelése
Már az előző
részben is és ebben a részben is használtuk az adattáblákat, de a
későbbiekben jobban bele kell mélyednünk ezek kezelésébe, annak érdekében,
hogy meg tudjunk bármilyen feladatot oldani.
Az adattáblák megadott
állapotban lehetnek, ezek az állapotok meghatározzák, hogy milyen
műveleteket végezhetünk el rajtuk.
A táblák mindaddig dsInactive állapotban vannak, amíg egy Open művelettel meg nem nyitjuk őket. Ekkor dsBrowse állapotba kerülnek, ami az adatok tallózását és
törlését teszi csak lehetővé. Amennyiben az adatokat módosítani
szeretnénk, akkor az Edit művelettel dsEdit állapotba kell kapcsolnunk, amikor is ezeket a
módosításokat elvégezhetjük. Előfordulhat, hogy új adatot kell felvenni,
ilyenkor vagy az éppen aktuális helyre szúrunk be egy rekordot az Insert művelettel, vagy a tábla végéhez fűzünk
hozzá egyet az Append
metódussal. Bármelyiket is használjuk, a tábla dsInsert üzemmódba kerül. Amikor végeztünk az adatok módosításával,
illetve felvételével, akkor a Post metódussal zárhatjuk azt le. Amennyiben ez nem sikerül, akkor a tábla
az éppen aktuális állapotban marad, ellenkező esetben visszatér a dsBrowse állapotába. Ugyanez a helyzet akkor is, amikor akár a
dsaInsert, akár a dsEdit állapotban meghívjuk a Cancel vagy a Delete metódust. Az előbbi megszakítja az éppen
aktuális változtatást, míg az utóbbi kitörli az éppen aktuális rekordot.
Most, hogy már ismerjük
ezeket az állapotokat, nézzük meg, hogy hogyan is tudunk mozogni az adattáblán
belül. Az, hogy melyik rekord az aktuális, egy mutató, a rekordmutató határozza
meg. A rekordmutató léptetésével tudjuk kiválasztani azt az egyedet, amellyel
dolgozni szeretnénk. Természetesen erre a feladatra is megfelelő metódusok
állnak rendelkezésre. A legelső rekordra viszi a mutatót a First metódus, míg a Last pontosan a legutolsó rekordot jelöli ki. A
következő rekordra ugrik a Next, míg az előzőre a Prior metódus
használatakor. Ezzel már meg is tudunk oldani minden műveletet, bármelyik
rekordot ki tudjuk választani.
Természetesen ezeket a
metódusokat az alkalmazásunk kódjában kell használnunk, de ugyanezeket tudjuk
elérni a DBNavigator
felhasználásával is.
A különböző
metódusok használatát mutatja be az alábbi lista:
Table1.Edit |
Table1.Post |
Table1.Insert |
Table1.Delete |
Table1.Cancel |
Table1.First |
Table1.Last |
Table1.Next |
Table1.Prior |
Az adatkezelés alapvető
feltétele, hogy hozzáférjünk a tábla mezőihez. Ehhez természetesen
szükségünk lesz a mező nevére és még két dologra. Az egyik, hogy a
mező nevét a FieldByName(’mezonev’) függvénnyel jelölhetjük ki, ami után meg kell adni,
hogy mit szeretnénk csinálni vele. A mező értékét a Value változó adja vissza. Ennek megfelelően, amikor
egy mező értékét szeretnénk megadni, akkor azt az alábbi formában tehetjük
meg:
DM.tblTabla_Neve.FieldByName(’MEZONEV’).Value:=mezo_erteke;
Amint látható a fenti
sorból, meglehetősen hosszú kódsort kell írnunk ahhoz, hogy egy mező
értékét megadhassuk. Ha visszaemlékeznek az Olvasók az első
példaprogramunkra, akkor tudhatják, hogy egy egyszerű tábla esetében is
számos mező értékét kell módosítani például egy új rekord felvételekor.
Mivel nagyon sokat kellene gépelni, ezért készíthetünk egy ciklust, aminek segítségével
ez a művelet jelentősen lerövidül.
Ennek a módja a következő:
with
DM.tblTabla_nev do
begin
táblakezelő metódusok;
end;
Ezt kibővíthetjük egy
vizsgálattal, ami megmondja nekünk, hogy a tábla már dsEdit vagy dsInsert állapotban van-e, mert ha nem, akkor nekünk kell
erről a megfelelő metódusokkal gondoskodnunk. Ez az alábbi módon
történhet meg:
with
DM.tblTabla_nev do
begin
if Not (State
in [dsEdit, dsInsert]) Then
Edit; //itt állítjuk a táblát
szerkesztő üzemmódba
táblakezelő metódusok;
end;
Keresés az adattáblákban
Ha adatokat tárolunk,
akkor gyorsan szembekerülhetünk azzal a feladattal, hogy a már meglévő
adatokból keressük meg a bizonyos feltételeknek megfelelő rekordokat.
Tulajdonképpen kétféle lehetőség kínálkozik ennek a feladatnak a megoldására.
A klasszikus értelemben vett keresésnél az első megfelelő rekordnál
megállítjuk a keresést. Amikor a rekordok számát szeretnénk valamilyen szempont
szerint lecsökkenteni, akkor a táblát szűrnünk kell. Mivel mindkét
művelet szinte minden esetben előfordul, ezért a továbbiakban
ismerkedjünk meg a keresési, majd a következő számban a szűrési
lehetőségekkel.
A Delphiben
többféle lehetőség közül választhatunk, amikor keresését kell
végrehajtanunk. A gyakorlatban ezekből leginkább kétféle eljárást használnak,
éppen ezért a továbbiakban ezeket mutatjuk be, ezek más és más felhasználási
körrel rendelkeznek.
A Locate függvény használatakor a rekordmutató az első
olyan rekordra ugrik, amely megfelel a megadott feltételeknek. A Locate logikai függvény, ami azt jelenti, hogy egy igaz vagy
egy hamis értékkel tér vissza attól függően, hogy a keresés eredményre
vezetett-e. Ennek megfelelően rendszerint egy feltételben használjuk,
annak érdekében, hogy ki tudjuk íratni, ha nincs találat.
with
DM.tblTabla_neve do
begin
if Not Locate
(’MEZONEV’, ’ertek’, [opciok]) then
ShowMessage
(’Nincs talállat’);
end;
A fenti, nagyon
egyszerű kód bemutatja általánosságban, hogyan kell használni a függvényt.
Amennyiben nincs találat, egy erre figyelmeztető hibaüzenet jelenik meg a
képernyőn (ShowMessage).
Előfordulhat olyan eset
is, amikor több mezőben szeretnénk feltételeket megadni. Ilyen esetben a
mezőneveket egymástól pontosvesszővel elválasztva felsoroljuk, majd
az értékeket egy tömbben helyezzünk el. Erre mutat példát az alábbi kódrészlet:
with
DM.tblTabla_neve do
begin
if Not Locate
(’MEZONEV1; MEZONEV2’, VarArrayOf([’ertek1’, ’ertek2’]),
[opciok]) then
ShowMessage
(’Nincs találat’);
end;
Mint látható, erre a célra a
VarArrayOf () függvényt kell
segítségül hívnunk.
Felmerülhet az olvasóban a
kérdés, hogy miért szerepel a függvényben az [opciok] kapcsoló? Azért, mert meg lehet adni két opciót is a Locate-nek. Az
egyikkel azt határozhatjuk meg, hogy szükségünk van-e a kis- és nagybetűk
megkülönböztetésére. Ezt a loCaseInsensitive paraméterrel tehetjük meg. Amennyiben használjuk ezt,
akkor a keresésnél a Delphi nem tesz különbséget a
kis- és a nagybetűk között. A másik opcióval, ami a loPartialKey névre hallgat, tudathatjuk a Delphivel,
hogy szótöredékekre is keresni kívánunk. Ez azt jelenti, hogy nem csak pontos
egyezésnél kapunk vissza eredményt. Ez hasznos lehet, amikor nem tudjuk a
pontos értékét a mezőnek, hanem csak egy részét, de mégis vigyázni kell
vele, mert könnyen adhat hamis találatot. Ne feledjük, hogy a Locate a rekordmutatót az első olyan rekordra viszi,
ami a feltételnek eleget tesz.
A másik keresési
lehetőség a Lookup
függvény használata, amely nagyon hasonlít működés szempontjából a Locate-re.
Ebben az esetben azonban a rekordmutató mozgatására nem kerül sor, a keresési
feltételnek megfelelő rekord megadott mezőjének értékét adja vissza
eredményül. Ennek a függvénynek nincsenek opciói, tehát ilyen
lehetőségeket nem adhatunk meg. Mivel itt nem logikai visszatérési
értékről van szó, éppen ezért máshogyan kell ezt a függvényt használni.
var Valtozo:
variant;
...
with
DM.tblTabla_Neve do
begin
Valtozo := Lookup
(’mezo_amiben_keresunk’,
’ertek_amit_keresunk’,
’eredmenymezo_neve’);
end;
Amint látható, definiálnunk
kell egy változót, aminek a típusa variant, mivel bármilyen típusú lehet. Azután ebbe a
változóba tesszük bele annak a mezőnek az értékét, ami a feltételnek
megfelelő rekordban található.
Több mező értékét is
megadhatjuk feltételnek, a szintaktika hasonló, mint amit a Locate függvénynél már megismertünk. Itt is felsoroljuk a
mezőneveket, majd egy tömbben elhelyezzük azok értékeit. Ezt mutatja az
alábbi kódrészlet:
var Valtozo:
variant;
...
with
DM.tblTabla_Neve do
begin
Valtozo := Lookup
(’MEZO1’, ’MEZO2’,
VarArrayOf ([’ERTEK1’,’ERTEK2’]), ’eredmenymezo_neve’);
end;
A fentiekből már
látható, hogy egyszerű eszközökkel nagyon gyorsan lehet az adatokat
megkeresni. Azonban nem mindig elég, ha egy rekordot keresünk meg, sok esetben
a rekordok számának a szűkítése a cél. Ez a művelet a szűrés,
amely annyira gyakori, hogy a következő számot ennek a témának szenteljük.
Markó Imre