Zgryźliwość kojarzy mi się z radością, która źle skończyła.
Rozdział 18.
Tworzenie aplikacji bazodanowych
C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\18.doc 705
Rozdzia³ 18. ¨ Tworzenie aplikacji bazodanowych 727
Trudno jest zawrzeć w krótkim rozdziale całość problematyki tworzenia aplikacji bazodanowych, więc i my z konieczności ograniczymy się tylko do najważniejszych kwestii.
Ponieważ wiesz już w jaki sposób tworzy się formularze baz danych, większą cześć tego rozdziału poświęcimy aspektom programowania baz danych na niższym poziomie. Na początku zapoznasz się z tworzeniem i wypełnianiem baz danych w sposób całkowicie programowy, następnie przejdziesz do zagadnienia modułów danych, by pod koniec rozdziału przyjrzeć się tworzeniu raportów baz danych przy użyciu komponentów z grupy QuickReport. Całość zostanie zamknięta omówieniem tematu dystrybucji aplikacji bazodanowych.
Niewizualny aspektprogramowania bazodanowego
Dotychczasowe ćwiczenia z poprzednich rozdziałów traktujących o programowaniu baz danych miały w przeważającej większości charakter prezentacyjny – ich zasadniczym przeznaczeniem było wyświetlanie i edycja danych. W niniejszej sekcji przyjrzymy się mniej spektakularnym – bo nie widocznym bezpośrednio – lecz równie ważnym mechanizmom.
Przykładowe ćwiczenia prezentowane w niniejszej sekcji ograniczają się z konieczności do komponentu TTable; pouczającym ćwiczeniem może być stworzenie analogicznych przykładów dla komponentu TQuery, z wykorzystaniem języka SQL.
Czytanie z bazy danych
Czytanie zawartości bazy danych nie jest w żadnym razie niczym nadzwyczajnym, toteż i pierwszy prezentowany przykład będzie również bardzo prosty – zawartość wybranych kolumn tabeli CUSTOMER.DB zostanie zapisana do pliku tekstowego, a poszczególne kolumny zostaną oddzielone przecinkami.
Trzeba w tym celu stworzyć obiekt klasy TTable, następnie skojarzyć go z konkretnym aliasem i konkretną tabelą w ramach aliasu. Kod wykonujący te operacje wygląda następująco:
var
Table : TTable;
begin
Table := TTable.Create(Self);
Table.DatabaseName := 'DBDEMOS';
Table.TableName := 'Customer.db';
end;
Powyższy fragment jest odpowiednikiem tego, co czyni Delphi podczas ustawiania właściwości DatabaseName i TableName w Inspektorze Obiektów.
Kolejnym krokiem będzie odczytanie każdego z rekordów bazy danych i zapisanie go do pliku w postaci linii tekstu. Najpierw zaprezentowana zostanie podstawowa struktura (bez rzeczywistego kodu) służąca do zapisywania pól bazy danych do pliku tekstowego, później przejdziemy do szczegółów. Ta podstawowa struktura wygląda następująco:
Table.Active := True;
while not Table.Eof do
begin
…
{Znajdujący się tutaj kod czyta wartości pól z bieżącego }
{rekordu i zapisuje je do pliku tekstowego }
…
Table.Next;
end;
Table.Free;
Działanie powyższego kodu jest niemal oczywiste. Przypisanie na początku wartości True właściwości Active powoduje otwarcie tabeli (identyczny efekt dałoby wywołanie metody Open). Następnie poszczególne rekordy tabeli są w ramach pętli odczytywane i zapisywane jako kolejne linie pliku tekstowego. Przejście do następnego rekordu odbywa się w wyniku wywołania metody Next, zaś kryterium zakończenia pętli jest badanie wyczerpania się rekordów (tj. próba przejścia poza ostatni rekord – metoda Eof zwraca wówczas wartość True). Po wykonaniu zadania obiekt Table jest zwalniany.
Naturalnie, żeby móc zapisać wartość każdego z pól do pliku tekstowego, wcześniej informację tę trzeba wydobyć z samego pola. W tym celu trzeba zastosować metodę FieldByName i właściwość AsString klasy TField. Była już o tym mowa w rozdziale 16. w sekcji „Dostęp do pól”. Pierwszym interesującym nas polem tabeli CUSTOMER.DB jest pole CustNo. Wydobycie wartości z tego pola może odbyć się w sposób następujący:
Obiekt klasy TTable nie musi być zwalniany w sposób jawny – zostanie bowiem automatycznie zwolniony w procesie zwalniania formularza; formularz jest jego komponentem‑właścicielem, co znajduje swe odzwierciedlenie w wywołaniu konstruktora:
Table := TTable.Create(Self);
(zmienna Self oznacza tutaj oczywiście formularz). Dobrą praktyką w programowaniu jest jednakże jawne zwalnianie niepotrzebnych już obiektów – co też uczynilismy.
var
S : string;
begin
S := Table.FieldByName('CustNo').AsString + ',';
Zauważ, że do otrzymanego w ten sposób łańcucha dodawany jest na końcu znak przecinka, dzięki czemu wartość pola zostanie odseparowana od następnych pól. Powyższy kod powtórzony zostanie dla pozostałych pól, których wartości nas interesują. Cała sekwencja poleceń została przedstawiona w dwóch modułach (listingi 18.1 i 18.2) składających się na program o nazwie MakeText, który został również umieszczony na dołączonej do książki dyskietce. Ten krótki program przetwarza tabelę CUSTOMER.DB i na jej podstawie tworzy plik tekstowy o nazwie CUSTOMER.TXT. Listing 18.1 przedstawia moduł formularza głównego (MakeTxtU.pas); w formularzu tym znajduje się jedynie przycisk i komponent Memo. Przyjrzyj się wpierw obydwu listingom, później przystąpimy do omówienia zasady ich działania.
Listing 18.1. MakeTxtU.pas
unit MakeTxtU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, DbTables;
type
TForm1 = class(TForm)
CreateBtn: TButton;
Memo: TMemo;
procedure CreateBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.CreateBtnClick(Sender: TObject);
var
Table : TTable;
S : string;
begin
{ Utworzenie obiektu Table oraz przypisanie mu nazwy bazy }
{ danych i nazwy tabeli. }
Table := TTable.Create(Self);
Table.DatabaseName := 'DBDEMOS';
Table.TableName := 'Customer.db';
{ Zmiana kursora na klepsydrę. }
Screen.Cursor := crHourGlass;
{ Można użyć komponentu Memo do pokazania postępu zadania}
{ i jednocześnie zapisania pliku na dysk. Najpierw obiekt }
{ Memo musi zostać wyczyszczony z wszelkiego tekstu. }
Memo.Lines.Clear;
{ Otwarcie tabeli. }
Table.Active := True;
CreateBtn.Enabled := False;
{ Pętla zapisująca kolejne rekordy do komponentu Memo. }
while not Table.Eof do begin
{ Pobranie pierwszego pola i dodanie go do łańcucha S, }
{ umieszczenie na końcu łańcucha znaku przecinka. }
S := Table.FieldByName('CustNo').AsString + ',';
{ Powtórzenie identycznego kroku dla wszystkich (wymaganych
{ przez nas) pól. }
S := S + Table.FieldByName('Company').AsString + ',';
S := S + Table.FieldByName('Addr1').AsString + ',';
S := S + Table.FieldByName('Addr2').AsString + ',';
S := S + Table.FieldByName('City').AsString + ',';
S := S + Table.FieldByName('State').AsString + ',';
S := S + Table.FieldByName('Zip').AsString + ',';
S := S + Table.FieldByName('Phone').AsString + ',';
S := S + Table.FieldByName('FAX').AsString + ',';
{ Dodanie łańcucha do komponentu Memo. }
Memo.Lines.Add(S);
{ Przejście do kolejnego rekordu. }
Table.Next;
end;
{ Zapisanie zawartości komponentu Memo do pliku tekstowego }
Memo.Lines.SaveToFile('customer.txt');
{ Przywrócenie poprzedniego kursora i jego ponowne udostępnienie }
CreateBtn.Enabled := True;
Screen.Cursor := crDefault;
Table.Free;
end;
end.
Cała akcja rozgrywa się we wnętrzu metody CreateBtnClick. W wyniku kliknięcia na przycisku Create File (utwórz plik),program wydobywa dane z tabeli bazy danych i umieszcza je w komponencie Memo. Jako pierwsza w łańcuchu umieszczana jest wartość pola CustNo, za nią dodawany jest przecinek. Potem do końca łańcucha dołączane są konsekwentnie kolejne wartości pól, po których ponownie występuje przecinek. Kiedy wszystkie dane zostaną odczytane z rekordu, łańcuch jest dodawany do komponentu Memo (jako jego kolejna linia) przy użyciu metody Add. Po osiągnięciu końca tabeli zawartość komponentu Memo jest zapisywana do pliku tekstowego.
Komponent Memo został tutaj użyty z dwóch powodów. Po pierwsze, wyświetlanie wyników w jego oknie (p. rys. 18.1) umożliwia obserwowanie tego, co generuje program. Po drugie, właściwość Lines (klasy TStrings) oferuje łatwy sposób zapisania poszczególnych linii do pliku tekstowego.
Rysunek 18.1.
Program MakeText w czasie pracy
Tworzenie tabel baz danych w sposób programowy
Wygodnym narzędziem do tworzenia baz danych jest np. Database Desktop. Niekiedy jednak aplikacja musi tworzyć tabele w sposób programowy (na przykład w sytuacji, gdy zestaw i właściwości pól tabeli nie są znane a priori przed wykonaniem programu). Utworzenie tabeli w kodzie programu wymaga następujących czynności:
1. Utworzenia aliasu BDE dla bazy danych.
2. Utworzenia obiektu klasy TTable.
3. Dodania definicji pól do właściwości FieldDefs.
4. Dodania definicji indeksów do właściwości IndexDefs (jeżeli zakładamy, że tabela będzie posiadać indeksy).
5. Utworzenia rzeczywistej tabeli przy użyciu metody CreateTable.
Tworzenie aliasu BDE i obiektu klasy TTablePierwsze dwa kroki miałeś okazję wykonać już wcześniej w rozdziale 16. kiedy mowa była o tworzeniu aliasów BDE oraz w poprzedniej sekcji, gdzie tworzyłeś obiekt klasy TTable. Poniżej znajduje się krótkie przypomnienie obu tych kroków:
var
Table : TTable;
begin
{ Utworzenie aliasu. }
CreateDirectory('C:\LPARADOX', nil);
Session.AddStandardAlias('MyDatabase', 'C:\LPARADOX',
'PARADOX');
{ zapisanie zmian w pliku konfiguracyjnym BDE }
Session.SaveConfigFile;
{ Utworzenie tabeli. }
Table := TTable.Create(Self);
Table.DatabaseName := 'MyDatabase';
Table.TableName := 'MyTable.db';
end;
W powyższym fragmencie kodu tworzony jest katalog LPARADOX (nazwa ta jest jedynie przykładowa), który następnie obsadzony zostaje w roli aliasu bazy danych typu Paradox.
Następnie tworzony jest obiekt klasy TTable, przy czym jego właściwości DatabaseName przypisywany jest utworzony przed chwilą alias, zaś właściwości TableName – nazwa tabeli, którą mamy zamiar utworzyć.
Na razie utworzyliśmy obiekt klasy TTable – sama tabela (jako plik dyskowy) jeszcze nie istnieje.
Definiowanie pólNastępny krok polega na zdefiniowaniu pól dla nowo tworzonej tabeli. Definicja ka...