W niniejszym rozdziale zajmiemy się obsługą zewnętrznych baz danych przez aplikacje stworzone za pomocą Delphi. Mimo iż do zrozumienia treści rozdziału konieczne jest pewne doświadczenie w pracy z bazami danych, to jednak może on stanowić wprowadzenie do tematyki tworzenia aplikacji bazodanowych wysokiej jakości. Również znawcy tematu znajdą tu ciekawe przykłady. Delphi 6 oferuje kilka mechanizmów dostępu do danych, które przedstawimy tu w zarysie, zaś w następnych rozdziałach zajmiemy się niektórymi z nich bardziej szczegółowo.
Wzorując się na systemie pomocy, możemy podzielić obsługiwane przez Delphi 6 technologie bazodanowe na poniższe cztery grupy, z których każda związana jest z określoną stroną w palecie komponentów:
· BDE — zawiera komponenty wykorzystujące mechanizm Borland Database Engine (BDE). Mechanizm ten oferuje szeroką gamę funkcji API organizujących współpracę z bazami danych. Znakomicie ułatwia obsługę baz danych Paradox i dBase, jest jednak najbardziej skomplikowany pod względem procedury instalacyjnej aplikacji końcowych.
· ADO — to grupa komponentów wykorzystujących ActiveX Data Objects (ADO), zapewniających dostęp do baz danych za pośrednictwem mechanizmu OLEDB. Obiekty ADO stanowią standard Microsoftu; dostępna jest bogata oferta sterowników zapewniających ich współpracę z rozmaitymi serwerami baz danych.
· dbExpress — technologia ta, posługując się efektywnymi sterownikami, zapewnia najszybszy dostęp do informacji przechowywanych w bazach danych. Komponenty tej grupy mają charakter uniwersalny, dostępne są także na platformie Linuksa. Uniwersalizm ten przesądza jednak o stosunkowo ubogim repertuarze możliwości w zakresie manipulowania danymi.
· InterBase — komponenty tej grupy umożliwiają bezpośredni dostęp do serwera InterBase, z pominięciem warstwy pośredniej (engine layer).
Z punktu widzenia aplikacji Delphi, przepływ informacji związanej z zarządzaniem bazami danych podzielić można na ściśle zdefiniowane etapy. Każdemu z nich odpowiada określona grupa komponentów realizujących specyficzne funkcje. Podział ten został schematycznie przedstawiony na rysunku 7.1.
Rysunek 7.1. Architektura bazy danych z punktu widzenia Delphi 6
Jak widać, interfejs użytkownika komunikuje się ze zbiorami danych poprzez pośrednią warstwę źródła danych (datasource), reprezentowaną przez komponent TDataSource. Każdy z omawianych na wstępie typów baz danych posługuje się innymi zbiorami danych, zaznaczonych w sposób symboliczny na rysunku 7.1; abstrakcyjnym komponentem reprezentującym zbiór danych jest komponent TDataSet[1].
Ostatnim ogniwem schematu na rysunku 7.1 jest połączenie z danymi, zapewniające fizyczny dostęp do informacji. Każdy z czterech wymienionych typów baz danych posługuje się innym komponentem połączeniowym, wywodzącym się z klasy TCustomConnection:
· TDataBase jest komponentem połączeniowym dla zbiorów danych opartych na technologii BDE — TTable, TQuery i TStoredProc. Jego wykorzystanie opisane zostało w rozdziale 28. książki „Delphi 4. Vademecum profesjonalisty” („Aplikacje bazodanowe typu klient-serwer”).
· TADOConnection realizuje połączenie z bazami danych ADO, jak MS Access czy MS SQL. Komponentami zbiorów danych ADO są TADODataSet, TADOTable, TADOQuery, i TADOStoredProc. Technologią ADO zajmiemy się dokładniej w rozdziale 9.
· TSQLConnection to komponent połączeniowy dla zbiorów danych opartych na technologii dbExpress. Są one efektywnymi, jednokierunkowymi (unidirectional) zbiorami danych reprezentowanymi przez komponenty TSQLDataSet, TSQLTable, TSQLQuery i TSQLStoredProc. Technologii dbExpress poświęcony jest rozdział 8. niniejszej książki.
· TIBDatabase jest komponentem połączeniowym dla zbiorów danych typu Interbase Express — TIBDataSet, TIBTable, TIBQuery i TIBStoredProc. Zrezygnowaliśmy w niniejszej książce z opisu komponentów Interbase Express, gdyż ich realizacja stanowi w dużej części naśladownictwo innych metod połączeniowych.
Elementy wspólne dla wszystkich rodzajów połączeń składają się na definicję klasy TCustomConnection. Zawiera ona metody, właściwości i zdarzenia związane z:
· nawiązywaniem i rozłączaniem połączenia z repozytoriami danych,
· logowaniem i nawiązywaniem bezpiecznego połączenia,
· zarządzaniem danymi.
Jest oczywiste, iż mimo owych wspólnych elementów funkcjonalnych, każdy z wymienionych komponentów połączeniowych posiada pewne charakterystyczne cechy, wynikające ze specyfiki docelowego repozytorium danych. Połączenie realizowane dla komponentów ADO różni się więc pod wieloma względami od połączenia realizowanego dla komponentów BDE — o czym możesz się przekonać studiując rozdziały 8. i 9. niniejszej książki oraz rozdział 28. „Delphi 4. Vademecum profesjonalisty”.
Zbiór danych (dataset) może być postrzegany jako dwuwymiarowa struktura kolumn (columns) i wierszy (rows). Każda kolumna, zwana też polem ( field) grupuje w sobie dane tego samego rodzaju, natomiast kolejne wiersze, zwane też rekordami (records), stanowią kolejne pozycje danych w zbiorze. Komponentem VCL, ujmującym w sposób abstrakcyjny ideę zbioru danych jest komponent TDataSet, zawierający właściwości i metody niezbędne do nawigowania wśród danych i manipulowania nimi, i stanowiący tym samym klasę bazową dla komponentów realizujących konkretne zbiory danych związane z różnorodnymi technologiami bazodanowymi.
Aby uniknąć niejednoznaczności w dalszej części lektury, musimy zdefiniować kilka niezbędnych pojęć i używać ich tylko w tym znaczeniu. Oto one:
· Zbiór danych (dataset) jest — zgodnie z tym, co powiedziano przed chwilą — uporządkowaną kolekcją rekordów; organizację każdego rekordu, taką samą dla wszystkich rekordów, określają jego pola, z których każde reprezentuje daną określonego typu (liczbę całkowitą, łańcuch znaków, liczbę w postaci znakowo-dziesiętnej, grafikę itp.).
· Tabela (table) jest specjalnym typem zbioru danych, najczęściej posiadającym fizyczną postać pliku dyskowego. Komponentami reprezentującymi różnorodne tabele są m.in. TTable, TADOTable, TSQLTable i TIBTable.
· Zapytanie (query) również stanowi konkretyzację zbioru danych, jednak nie „zmaterializowaną” w tak ścisły sposób jak tabela, lecz stanowiącą swego rodzaju „tabelę tymczasową”, która jest (zazwyczaj) wynikiem żądania skierowanego pod adresem serwera. Zapytanie reprezentowane jest m.in. przez komponenty TQuery, TADOQuery, TSQLQuery i TIBQuery.
Notatka
Jak wspominaliśmy na wstępie, niniejszy rozdział nie jest przeznaczony dla nowicjuszy i nie zawiera obszernego wprowadzenia w problematykę programowania obsługi baz danych. Jeżeli określenia: baza danych, tabela czy indeks brzmią dla Ciebie raczej obco, powinieneś uzupełnić swą wiedzę korzystając z podręczników bardziej podstawowych[2].
Jakakolwiek operacja na zbiorze danych musi być poprzedzona jego otwarciem. Zadanie to wykonuje metoda Open() klasy TDataSet:
Table1.Open;
Wywołanie to jest równoważne ustawieniu na True właściwości Active:
Table1.Active := True;
Czynnością wieńczącą wszystkie operacje na zbiorze danych jest jego zamknięcie, dokonywane przez wywołanie metody Close()
Table1.Close;
równoważne ustawieniu właściwości Active na False:
Table1.Active := False;
Wskazówka
Jeżeli wykorzystujesz zbiory danych zlokalizowane na serwerze SQL, otwarcie pierwszego ze zbiorów tego serwera poprzedzone jest nawiązaniem połączenia (connection) z serwerem, zaś zamknięcie ostatniego z tych zbiorów powoduje rozłączenie się z serwerem (disconnection). Ponieważ operacje połączenia i rozłączenia wymagają zawsze nieco czasu, korzystne może okazać się użycie komponentu TDatabase w celu nawiązania permanentnego połączenia z serwerem w sytuacji, kiedy opisane przed chwilą połączenia (rozłączenia) zdarzałyby się nazbyt często. Niebawem powrócimy do tego zagadnienia.
O tym, jak podobne jest otwieranie (zamykanie) różnych typów zbiorów danych, zaświadczyć może kod prezentowany na wydruku 7.1.
Wydruk 7.1. Otwieranie i zamykanie przykładowych zbiorów danych
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, FMTBcd, DBXpress, IBDatabase, ADODB, DBTables, DB, SqlExpr,
IBCustomDataSet, IBQuery, IBTable, StdCtrls;
type
TForm1 = class(TForm)
SQLDataSet1: TSQLDataSet;
SQLTable1: TSQLTable;
SQLQuery1: TSQLQuery;
ADOTable1: TADOTable;
ADODataSet1: TADODataSet;
ADOQuery1: TADOQuery;
IBTable1: TIBTable;
IBQuery1: TIBQuery;
IBDataSet1: TIBDataSet;
Table1: TTable;
Query1: TQuery;
SQLConnection1: TSQLConnection;
Database1: TDatabase;
ADOConnection1: TADOConnection;
IBDatabase1: TIBDatabase;
Button1: TButton;
Label1: TLabel;
Button2: TButton;
IBTransaction1: TIBTransaction;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
procedure OpenDatasets;
procedure CloseDatasets;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
IBDatabase1.Connected := True;
ADOConnection1.Connected := True;
Database1.Connected := True;
SQLConnection1.Connected := True;
procedure TForm1.Button1Click(Sender: TObject);
OpenDatasets;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
CloseDatasets;
IBDatabase1.Connected := false;
ADOConnection1.Connected := false;
Database1.Connected := false;
SQLConnection1.Connected := false;
procedure TForm1.CloseDatasets;
// rozłączenie ze zbiorami dbExpress
SQLDataSet1.Close; // lub Active := false;
SQLTable1.Close; // lub Active := false;
SQLQuery1.Close; // lub Active := false;
// rozłączenie ze zbiorami ADO
ADOTable1.Close; // lub Active := false;
ADODataSet1.Close; // lub Active := false;
ADOQuery1.Close; // lub Active := false;
// rozłączenie ze zbiorami Interbase Express
IBTable1.Close; // lub Active := false;
IBQuery1.Close; // lub Active := false;
IBDataSet1.Close; // lub Active := false;
// rozłączenie ze zbiorami BDE
Table1.Close; // lub Active := false;
Query1.Close; // lub Active := false;
Label1.Caption := 'Zbiory danych są zamknięte.'
procedure TForm1.OpenDatasets;
// połączenie ze zbiorami dbExpress
SQLDataSet1.Open; // lub Active := true;
SQLTable1.Open; // lub Active := true;
SQLQuery1.Open; // lub Active := true;
// połączenie ze zbiorami ADO
ADOTable1.Open; // lub Active := true;
ADODataSet1.Open; // lub Active := true;
ADOQuery1.Open; // lub Active := true;
// połączenie ze zbiorami Interbase Express
IBTable1.Open; // lub Active := true;
IBQuery1.Open; // lub Active := true;
IBDataSet1.Open; // lub Active := true;
// połączenie ze zbiorami BDE
Table1.Open; // lub Active := true;
Query1.Open; // lub Active := true;
Label1.Caption := 'Zbiory danych są otwarte.';
procedure TForm1.Button2Click(Sender: TObject);
end.
Powyższy moduł jest częścią projektu ...
b.senni