Um die grundlegende DB – Zugriffstechnik unter Delphi kennenzulernen, empfiehlt es sich eine einfach zu bedienende Datenbank zu verwenden. Hier kommt z.B. Absolute Database in Frage, welche als Personal Edition kostenlos zur Verfügung steht und entweder von der Webseite www.componentace.com heruntergeladen werden kann, oder aber auch von der Partner-DVD Delphi 2009 bezogen werden kann.
Die kostenlose Version darf aber nur nicht-kommerziell und für persönliche Zwecke eingesetzt werden.
Die Datenbank selber kann auch als netzwerkfähige Version erworben werden, für Single-User Anwendungen oder/und bei einer begrenzten Anzahl von erwarteten Nutzer auch im kommerziellen Rahmen eine durchaus interessante Alternative, die man technologisch mit einer VFP Datenbank vergleichen kann, weswegen ich Sie auch für diesen Teil meiner Serie ausgewählt habe.
Am Anfang steht die Datenbank
Zunächst gilt es eine Datenbank anzulegen. Hierzu kann man den beigelegten Datenbank-Manager unter Utils\bin – Verzeichnis Ihrer Absolute Database Installation verwenden. Dies geschieht über File\Create Database. Vergeben Sie einen sinnvollen Namen, z.B. Adressbuch, die Vorgabewerte können erstmal übernommen werden, und speichern Sie Ihre Datenbank. Die Datenbank mit der Endung *.Abs kann man mit einem DBC-Container vergleichen. Ein wesentlicher Unterschied ist aber, dass Absolute Database alle Tabellen und Indexdateien in einer Datei speichert. Diese Vorgehensweise gilt aber nicht generell unter Delphi, sondern hängt von der Datenbank ab.
Einmal angelegt können Sie über Table / Create Table nun eine Tabelle anlegen. Dies ist ähnlich wie unter VFP und sollte daher keine Probleme bereiten. In unserem Fall legen wir eine einfache Adresstabelle an (ID als Autoinc, Name,Vorname,Plz,Ort, Straße… als String-Felder).
In Delphi gilt es zunächst eine leere VCL-Formular Anwendung anzulegen. Standardmäßig haben Sie zunächst nur ein Formular. Fügen Sie dem Projekt nun noch ein Datenmodul hinzu, welches man mit dem VFP-Dataenvironment vergleichen kann. Projekt-Explorer -> Rechtsklick und hier dann Neue hinzufügen – Weitere – Datenmodul. Vergeben Sie für das leere Datenmodul einen Namen, z.B. dmMain und speichern Sie es. z.B. unter den Namen Datamod. Fügen Sie das neue Modul der Uses-List Ihres Hauptformulars hinzu, damit Sie darauf zugreifen können.
unit mainform;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,datamod;
Fügen Sie nun dem Datenmodul aus der Palette Absolute Database folgende Komponenten hinzu:
TAbsDatabase, TAbstable, TAbsQuery und aus der Palette Datenzugriff ein TDataSource.
Um die Datenbank programmatisch zu öffnen, doppelklicken Sie auf das Datenmodul, Delphi erzeugt dann automatisch eine Ereignisprozedur für OnCreate (d.h. alles was hier steht wird ausgeführt, wenn das Formular erzeugt wird). Unter der Voraussetzung, dass Ihre Datenbank im gleichen Verzeichnis wie das Programm liegt (bei einer echten Anwendung sollte man Daten und Programm natürlich trennen), geben Sie folgenden Code ein:
procedure TdmMain.DataModuleCreate(Sender: TObject);
begin
absDatabase1.DatabaseFileName:=ExtractFilePath(ParamStr(0))+’\Adressbuch.abs‘; //weist die Datenbankdatei zu
absDatabase1.Connected:=false; // um sicher zu gehen, dass keine Verbindung aus Design-Zeit mehr offen ist
abstable1.Active:=false;
absDatabase1.DatabaseName:=’Adressbuch‘;
absDatabase1.Connected:=True; // Stellt die Verbindung her
abstable1.Open;
end;
Sinnvollerweise sollte man auch gleich an das Beenden der Verbindung denken. Das Gegenstück zu OnCreate ist OnDestroy. Die Eigenschaften und Ereignisse des Moduls finden Sie im Objektinspektor.
procedure TdmMain.DataModuleDestroy(Sender: TObject);
begin
absDatabase1.Connected:=false;
end;
Nun ist zwar eine Datenbankverbindung hergestellt, aber noch keine Tabelle geöffnet.
Wechseln Sie zur Designansicht Ihres Datenmoduls:
Geben Sie für die Database – Komponente einen Namen unter DatabaseName ein. Dieser ist frei wählbar, im Beispiel Adressbuch.
Für die Table-Komponente wählen Sie diese Datenbank nun unter der Eigenschaft DatabaseName aus. Danach können Sie eine Tabelle auswählen (Tablename), in unserem Falle gibt es nur eine Tabelle Adressen. Optional können Sie einen Index zuweisen, falls Sie einen erstellt haben. Dieser kann natürlich zur Laufzeit gewechselt werden.
Nun wechseln Sie zu Ihrer TDataSource-Komponente und weisen Sie dieser unter Dataset Ihre Table-Komponente zu. TDataSource stellt das Bindeglied zwischen Ihrer Tabelle und den diversen Eingabekomponenten von Delphi, z.B. DBEdit dar. Wechseln Sie wieder zur Codeansicht und ergänzen Sie die Create-Prozedur, um die Zeile
abstable1.Open;
und Destroy um die Zeile
abstable1.close;
(Beides war weiter oben bereits berücksichtigt)
Wenn Sie das Programm nun starten, wird die Datenbank und die Tabelle automatisch geöffnet, beim Schließen wieder alles geschlossen.
Um dem Ganzen nun ein wenig Leben einzuhauchen gilt es das Hauptformular etwas aufzupeppen.
Fügen Sie dem Formular 8 Buttons hinzu.
Empfehlenswert:
Ziehen Sie zwei TImageList – Komponenten auf Ihr Datenmodul
Benennen Sie eine ImageEnabled, die andere ImageDisabled
Fügen Sie nun 8 BMPs oder PNGs hinzu mit vergleicbaren Symbolen wie im folgenden Bild:
Für die Disabled-Imagelist sollten Sie entsprechende Graustufen Varianten Ihrer Bilder verwenden.
Weisen Sie nun der Toolbar unter Images die Bildliste ImageEnabled und unter DisabledImages die Bildliste ImageDisabled zu.
Der Vorteil einer zentral abgelegten Image-Verwaltung liegt darin, dass Ihr Programm weniger Resourcen verbrauchen wird, da die Bilder nur einmal dem Programm hinzugefügt werden. Für sich selber ersparen Sie sich selber Sucharbeit, wenn Ihr Programm fortgeschritten ist und Sie immer wieder auf Bilder zugreifen wollen.
Formulare und Datenmodule manuell zur Laufzeit erzeugen
Ein Wort zu den Datenmodulen, wenn Ihr Programm komplexer wird:
Sinnvoll ist es ein Hauptdatenmodul anzulegen, dieses enthält Ihre TDatabase-Komponenten, für Ihre Forumlare sollten Sie dann jeweils eigene Datenmodule mit jeweils eigenen TTable/TQuery-Referenzen anlegen, auch um die Daten der einzelnen Formulare voneinander zu trennen. Dies entspricht in seiner Wirkung dem, was Sie unter VFP über DataSession erreichen, wenn Sie hier ‚Private Data Sessions‘ wählen.
In unserem Fall erzeugt Delphi Datenmodul und Form beim Start automatisch, in unserem Fall ist das i.O., da wir ohnehin nur ein Formular haben. Im Regelfall werden Sie dies bei einer komplexeren Applikation aber nicht wollen, in diesem Fall sollten Sie das Datenmodul im Create – Ereignis des Formulars erzeugen.
Hier sollten Sie zunächst Delphi mitteilen, dass das Modul (oder auch eine Form) nicht automatisch erzeugt wird.
Dies geschieht über Projekte – Optionen – Formulare.
In Create sollten Sie dann
if dmMain=Nil then
dmMain:=TdmMain.Create(nil);
ergänzen und im OnClick-Event unserer Programm-Beenden Schaltfläche
dmMain.Free;
dmMain:=Nil; // damit auch der Zeiger gelöscht wird, da ansonsten die Abfrage dmMain=Nil fehlschlägt
Nach diesen Vorarbeiten sollten Sie nun eine einfache Datenmaske anlegen. Ziehen Sie dazu eine TGroupBox auf die Form und anschließend enstprechend den Feldern Ihrer Tabelle TDBEdit-Komponenten, sowie
einige Labels zur Beschriftung (bei den TMS-Komponenten sind bei den dortigen Komponenten oft schon Labels in der Komponente mit enthalten).
Markieren Sie dann alle TDBEdit-Komponenten und weisen Sie diese Ihre TDataSource-Komponente in der Eigenschaft DataSource zu. Unter Datafield die entsprechenden Tabellenfelder.
Markieren Sie dann die Schaltflächen für ‚Speichern‘ und ‚Verwerfen‘ und setzen Sie Enabled auf False.
Für die Schaltfläche ‚Programm beenden‘ wählen Sie das Click-Ereignis, doppelklicken es, damit Delphi in die Code-Ansicht wechselt und den entsprechenden Event-Rumpf erzeugt:
procedure TForm1.BtnSchliessenClick(Sender: TObject);
begin
dmMain.Free;
dmMain:=Nil;
application.Terminate;
end;
Nun ist das Basis-Programm fertiggestellt.
Status einer Tabelle : State
Zunächst gilt es die Schaltfläche entsprechend des jeweiligen Zustandes Ihrer Tabelle anzupassen.
Es gibt im wesentlichen drei Möglichkeiten:
- dsEdit : Ihre Tabelle befindet sich im Bearbeitungsmodus (eines bestehenden Datensatzes)
- dsBrowse: Ihre Tabelle bedindet sich im Blättermodus, der Standardzustand
- dsInsert: Ihre Tabelle befindet sich im Einfügemodus (eines neuen Datensatzes)
Abfragen lässt sich der Zustand immer mit
if abstable1.state = dsEdit
if abstable1.state = dsBrowse
if abstable1.state = dsInsert
Zudem bietet die TDataSource eine Eigenschaft OnStateChange:
Hier könnte man folgenden Code hinzufügen
if abstable1.active=False then // um sicher zustellen, dass die Tabelle auch offen ist, sonst gäbe es eine Exception
exit;
frmMain.BtnNeu.Enabled:=True;
frmMain.BtnSpeichern.Enabled:=False;
frmMain.BtnVerwerfen.Enabled:=false;
frmMain.BtnLoeschen.Enabled:=True;
if (abstable1.State<>dsBrowse) then
begin
frmMain.BtnNeu.Enabled:=false;
frmMain.BtnSpeichern.Enabled:=true;
frmMain.BtnVerwerfen.Enabled:=true;
frmMain.BtnLoeschen.Enabled:=false;
end;
Damit Sie vom Datenmodul aus auf Elemente des Formulars zugreifen können, müssen Sie das Formular zunächst dem Datenmodul bekanntmachen. Hierzu fügen Sie unter ‚Implementation‘ folgenden Code-Abschnitt hinzu:
implementation
{$R *.dfm}
uses mainform;
Sie können Mainform nicht der Uses-List im Interface Abschnitt hinzufügen, da Delphi dies als zirkuläre Referenz verweigern würde, da das Datenmodul bereits dem Formular in dessen Uses-List zugewiesen wurde.
Daten hinzufügen / Löschen / Speichern….
Für die eigentlichen Tabellenbearbeitungsfuuktionen sollten Sie eigene Prozeduren/Funktionen anlegen. Dies am Besten im Datenmodul, um diese Bearbeitungsfunktionen weitgehend von der Benutzerschnittstelle zu trennen.
Fügen Sie hierfür vier Prozeduren im Public – Abschnitt des Datenmoduls hinzu:
Procedure TabelleNeu;
Procedure TabelleSpeichern;
Procedure TabelleVerwerfen;
Procedure TabelleLoeschen;
Im Implementationsteil erstellen Sie den Code für die vier Prozeduren:
Procedure TdmMain.TabelleNeu;
begin
if abstable1.Active=false then exit;
try
abstable1.insert;
frmMain.txtVorname.setfocus;
Except
on e: exception do
dialogs.showmessage(e.message);
end;
end;
Procedure TdmMain.TabelleSpeichern;
begin
IF abstable1.active=False then exit;
if abstable1.State=dsBrowse then
exit;
Try
abstable1.Post;
except
on e: exception do
begin
abstable1.Cancel;
dialogs.showmessage(e.message);
end;
End;
end;
Procedure TdmMain.TabelleVerwerfen;
begin
IF abstable1.active=False then exit;
if abstable1.State=dsBrowse then
exit;
abstable1.Cancel;
end;
Procedure TdmMain.TabelleLoeschen;
begin
try
IF abstable1.active=False then exit;
if abstable1.Eof=True then exit;
if abstable1.State<>dsBrowse then exit;
abstable1.Delete;
except
on e: exception do
showmessage(e.Message)
end;
end;
Daneben brauchen Sie noch vier Routinen für die Tabellensteuerung:
procedure TdmMain.TabellePosStart;
begin
abstable1.First;
end;
procedure TdmMain.TabellePosEnde;
begin
abstable1.Last;
end;
procedure TdmMain.TabellePosWeiter;
begin
abstable1.Next;
end;
procedure TdmMain.TabellePosZurueck;
begin
abstable1.Prior;
end;
Alle Routinen sind über das Click-Ereignis mit der jeweiligen Schaltfläche zu verbinden, z.B. für die Neu-Schaltfläche: dmMain.TabelleNeu;
Selbstverständlich kann das Ganze natürlich noch detailreicher gestaltet werden, aber dies bleibt Ihnen überlassen.
Weitere interessante Befehle / DB-Relevantes, was VFP Anwender bekannt vorkommen sind:
table.eof -> Eof()
table.bof -> Bof()
table.locate – Locate // sofern es sich um ein Indexfeld handelt, wird dies bei Delphis Version berücksichtigt
Programmgesteuertes Editieren:
table.edit;
table.fieldbyname(‚Feld‘).AsString:=“; (oder AsInteger / AsDatetime… je nach Feldtyp)
table.post;
Entspricht in VFP
replace table.feld with „“
TableUpdate()
Transaktionen:
Gibt es natürlich auch unter Delphi, die Ausgestaltung und Möglichkeiten sind aber abhängig von den jeweiligen Datenbanken. In unserem obigen Beispiel für die Löschfunktion z.B.:
Procedure TdmMain.TabelleLoeschen;
begin
try
IF abstable1.active=False then exit;
if abstable1.Eof=True then exit;
if abstable1.State<>dsBrowse then exit;
absdatabase1.StartTransaction;
abstable1.Delete;
absdatabase1.Commit(True);
except
on e: exception do
begin
showmessage(e.Message);
absdatabase1.Rollback;
end;
end;
end;
Unter Delphi sind alle Tabellenzugriffe immer an eine Komponente gebunden, von daher ist eine Tabelle, wie auch bei Anbindung an einer Query, ein Objekt. Da alle Anweisungen immer direkt an der Komponente gekoppelt sind, sind diese auch eindeutig. Ein VFP Desaster a la
select meineTabellex && gedacht war select meineTabelle
Delete
wo dann u.U. ein Delete in der falschen Tabelle ausgeführt wird, kann so unter Delphi nicht passieren.
Natürlich liegt es in VFP am Entwickler alle Befehler richtig zu definieren, keine Fehler zu machen, aber Fehler passieren immer, von daher schön, wenn eine Entwicklungsumgebung und Ihr Konzept einem hilfreich zur Seite steht.
Insgesamt gestaltet sich das Arbeiten mit Tabellen unter Delphi sehr einfachund auch sehr ähnlich wie unter VFP. Das Ganze m.E. aber wesentlich durchdachter, logischer, weniger kryptisch und intuitiver als unter .NET.
Der nächste und wahrscheinlich abschließende Teil beschäftigt sich dann mit Queries (SQL basierter Zugriff).