Programowanie w języku Delphi » Artykuły

Oracle 10g i Delphi 7. Programowanie baz danych

  • 2006-02-19 18:38
  • 10 komentarzy
  • 4344 odsłony
  • Oceń ten tekst jako pierwszy
Oracle 10g to kolejna wersja chyba najbardziej znanej bazy danych. Jej stabilność i wydajność to już niemal legenda. W oparciu o Oracle tworzone są setki aplikacji bazodanowych operujących na milionach rekordów. Tego typu aplikacje często muszą być przygotowane w krótkim czasie. Do takich właśnie zastosowań doskonale nadaje się Delphi -- środowisko programistyczne umożliwiające szybkie tworzenie aplikacji. Dzięki zaangażowaniu firm trzecich powstały komponenty, moduły i biblioteki pozwalające na współpracę Oracle i Delphi oraz sprawne budowanie aplikacji bazodanowych łączących te platformy.

Spis treści

     1 ORACLE 10g
     2 BDE
          2.1 Parę słów o BDE
          2.2 Tworzenie połączenia z bazą danych
          2.3 Komponent Table
          2.4 Specjalne cechy komponentu Table
          2.5 Metoda Locate
          2.6 Podsumowanie BDE
     3 ADO
          3.1 Wstęp do ADO
          3.2 ADOConnection i łączenie z bazą
          3.3 Pliki DataLink i przenośność aplikacji ADO
          3.4 Przykładowa aplikacja ADO
          3.5 Zapytania z parametrami
          3.6 Podsumowanie technologii ADO
     4 dbExpress
          4.1 Parę słów o dbExpress
          4.2 Nawiązywanie połączenia z bazą danych
          4.3 SQLQuery i wyświetlanie danych
          4.4 Edycja danych
          4.5 Podsumowanie dbExpress
     5 ODAC
          5.1 Wstęp
          5.2 Instalacja
          5.3 Paleta komponentów ODAC
          5.4 Tworzenie połączenia z bazą danych
          5.5 OraSQL
          5.6 Podsumowanie ODAC
     6 Tworzenie niestandardowej siatki
          6.1 Dopasowanie szerokości kolumn
     7 Raportowanie bazy danych Oracle
          7.1 Rave Reports - raportowanie za pomocą kodu
     8 Podsumowanie


Mnóstwo informacji na temat Oracle 10g, programowania tej bazy danych w Delphi i raportowaniu za pomocą Rave Reports znajdziecie w książce "Oracle 10g i Delphi. Programowanie baz danych", której autorami są Artur Mościcki i Igor Kruk.
Więcej informacji o ksiażce znajdziecie na stronie
http://helion.pl/ksiazki/oradel.htm



ORACLE 10g


Nie będę tu przedstawiał zagadnień związanych z instalacją i konfiguracją Oracle 10g (znajdziecie to w książce), a poza tym, w przypadku Oracle 10g, nie jest to proces bardzo skomplikowany. Skupię się tylko na przedstawieniu dlaczego warto poznawać Oracle oraz przedstawię podstawy pracy.

Oracle 10g to system baz danych z wyższej półki, za którym stoi potężna korporacja zrzeszająca dziesiątki tysięcy wykwalifikowanych pracowników i programistów, ciągle ulepszających swój produkt. Oracle 10g wyróżniają następujące cechy:
  • wydajność bazy,
  • dostępność na każdą platformę,
  • przenośność (migracje),
  • skalowalność,
  • zautomatyzowanie czynności stricte administracyjnych,
  • proste użytkowanie,
  • obszerna dokumentacja dostępna na stronie producenta,
  • silne wsparcie ze strony producenta i użytkowników (mnóstwo serwisów internetowych poświęconych Oracle i Oracle 10g),
  • dostępność partycjonowania, indeksu bitmapowego,
  • szereg dodatków, np. pakiet Oracle 10g Companion,
  • zaangażowanie się firm trzecich w tworzenie komponentów, modułów, bibliotek pozwalających na współpracę Oracle i różnych środowisk programowania, np. Delphi,
  • automatyczne monitorowanie i diagnostyka pracy serwera bazy danych ? mechanizm autodiagnozowania ADDM (Automatic Database Diagnostic Monitor), monitorujący w regularnych odstępach wydajność bazy i w razie potrzeby analizujący przyczyny problemu i sugerujący przeciwdziałanie,
  • narzędzia doradcze (SQL Tuning Advisor i SQL Access Advisor), tworzące zalecenia dla optymalizacji wykonywania zapytań SQL,
  • wysokie bezpieczeństwo danych.

Dodatkowym atutem przemawiającym za poznaniem i przetestowaniem serwera baz danych Oracle 10g jest fakt, iż ponad 80% dużych komercyjnych witryn w Internecie działa właśnie w oparciu o Oracle.

Jeszcze słowo o wymaganiach:
  • minimum 256 MB pamięci operacyjnej,
  • ilość pamięci wirtualnej dwukrotnie większa od ilość RAM-u,
  • minimum 100 MB wolnego miejsca w katalogu tymczasowym,
  • 1,5 GB przestrzeni dyskowej dla binariów i przykładowej bazy,
  • procesor co najmniej 200 MHz.

Oczywiście dobrze jest mieć lepsze parametry - u mnie na komputerze (384MB RAMu i procesor 800 MHz) Oracle działa bez problemów. Oczywiście należy pamiętać o tym że tuż po włączeniu kompa, Oracle nie będzie działał od razu - musi się najpierw wszystko pouruchamiać - a to trwa do kilku minut (na gorszych sprzętach).

Ok, znamy już zalety i wymagania Oracle - to teraz przedstawię najwiażniejsze podstawy pracy z Oracle 10g.

Najważniejszymi narzędziami w Oracle 10g są Enterprise Manager (do zarządzania Oraclem) i iSQL *Plus (do wykonywania instrukcji SQL, PL/SQL, pakietów itp) - oba narzędzia dostępne są z poziomu przeglądarki internetowej, co jest niewątpliwym plusem.
Aby uruchomić Enterprise Managera należy w oknie przeglądarki wpisać http://localhost:5500/em następnie podać login i hasło użytkownika (np. takie jak podczas instalacji Oracle) i można już administrować Oracle 10g - oczywiscie nie przesadzajcie z dłubaniem w oracle - bo można go uszkodzić :)

Rysunek

Natomiast żeby odpalić iSQL *Plusa trzeba w oknie przeglądarki wpisać http://localhost:5560/isqlplus i znów podać login i hasło.

Rysunek

Oki, aby można było przetestować działanie przedstawionych w dalszej części artykułu programów proponuję stworzyć nowego użytkownika, nadać mu wszystkie możliwe uprawnienia i role i dodać do grupy administratorów (nie będę opisywał szczegółówo, jak się to robi - powiem tylko że można to zrobić w Enterprise Managerze - po szczegółowe informacje odsyłam do dokumentacji Oracle lub do książki). Nie jest to takie trudne - jak otworzysz Enterprise managera to od razu zobaczysz odpowiednie linki. oczywiście wszystkie role i uprawnienia nie są konieczne do tego by zaprezentowane przykłady zadziałały, ale dzięki temu stworzymy administratora Oracle, który będzie mógł zrobić praktycznie wszystko w racle, więc wcześniej czy później w pracy z Oracle zetkniesz się z tym zagadnieniem. Użytkownika tego (jego loginu i hasła) proponuję używać również w dalszej części tego artykułu.

No i zostało nam jeszcze stworzenie przykładowych tabel w Oracle. Powiedzmy, że będą to tabele przechowujące informacje o książkach. Uruchom iSQL Plusa i wpisz poniższe polecenia w celu utworzenia tabel

CREATE TABLE Autorzy
(
id_autora         NUMBER(10) PRIMARY KEY,
imie              VARCHAR2(20) NOT NULL,
nazwisko          VARCHAR2(30) NOT NULL
);
 
 
CREATE TABLE Kategorie
(
id_kategorii      NUMBER(10) PRIMARY KEY,
nazwa             VARCHAR2(30) NOT NULL
);
 
CREATE TABLE Ksiazki
(
ISBN              VARCHAR2(50) PRIMARY KEY,
tytul             VARCHAR2(100) NOT NULL,
podtytul          VARCHAR2(100),
liczba_stron      NUMBER(10),
id_kategorii      NUMBER(10) REFERENCES Kategorie(id_kategorii),
recenzja          VARCHAR2(1000)
);
 
CREATE TABLE Autorzy_Ksiazki
(
id_autora_ksiazki NUMBER(10) PRIMARY KEY,
id_autora         NUMBER(10) REFERENCES Autorzy(id_autora),
ISBN              VARCHAR2(50) REFERENCES Ksiazki(ISBN)
);


Relacje pomiędzy tabelami widać praktycznie od razu, więc nie będę dokumentował ich rysunkiem.

Ok, to tyle jeśli chodzi o Oracle - wiem ze mało, ale czas przejść do dalszej części, czyli do technologii dostępu do baz danych. Dużo więcej informacji na temat Oracle 10g i pracy w nim, oraz o tworzeniu interfejsow WWW w czystym PL/SQL (bez użycia Delphi czy innego środowiska) znajdziecie w pierwszej części książki "Oracle 10g i Delphi. Prgramowanie baz danych".


BDE


Parę słów o BDE


BDE (Borland DataBase Enigme) to technologia, która została stworzona jako alternatywa dla technologii ODBC (tej już nie będę omawiał w tym artykule). Powstała przy współpracy kilku firm, m.in. Borland i Oracle. Kiedyś była to podstawowa technologia dostępu do baz danych, obecnie jest wypierana przez inne, takie jak ADO czy dbExpress. Zaletą BDE jest większa w porównaniu z ODBC szybkość działania oraz prostota obsługi, wadą zaś ? przenośność. Wynika to z tego, że BDE nie obsługuje systemu zarządzania baz danych bezpośrednio, lecz robi to poprzez pośrednika, jakim jest program SQL Links. Obecnie Borland rezygnuje z rozwijania tej technologii, niemniej można ją wykorzystać nawet do łączenia się z bazami danych stworzonymi w Oracle 10g. Przyjrzyjmy się palecie komponentów BDE dostępnych w Delphi 7.

Na zakładce BDE znajduje się parę komponentów
  • Table ? umożliwia pracę z tabelami bazy danych;
  • Query ? umożliwia wykonywanie zapytań SQL;
  • StoredProc ? umożliwia wykonywanie procedur składowanych na serwerze baz danych;
  • Database ? umożliwia zdefiniowanie połączenia z bazą danych;
  • Session ? służy do globalnej kontroli komponentów Database. Domyślnie tworzony jest automatycznie (właściwość SessionName komponentu Database ustawiana jest na Default). Jeśli jednak użytkownik tworzy aplikacje wielowątkową, to powinien dla każdego wątku używać oddzielnego komponentu;
  • BatchMove ? służy do kopiowania zbioru danych. Może być używany do konwersji tabel na różne formaty baz danych lub do tworzenia zbioru rekordów odłączonych;
  • UpdateSQL ? pozwala na wykonywanie instrukcji SQL (UPDATE). Buforuje zbiór danych;
  • NestedTable ? umożliwia hermetyzowanie zbioru danych zagnieżdżonego w innej tabeli.

Tworzenie połączenia z bazą danych


Aby stworzyć nowe połączenie z bazą danych wykorzystujące technologię BDE, należy użyć programu BDE Administrator. Program ten można uruchomić poza Delphi 7 poprzez Start/Programy/Delphi 7/BDE Administrator. Ukaże się wówczas okno programu (rysunek poniżej), na którym będą widoczne dwie zakładki DataBase i Configuration. Na zakładce Configuration można zobaczyć, jakie sterowniki SQL Links (gałąź Drivers/Native) i ODBC (gałąź Drivers/ODBC) są dostępne



Przejdź na zakładkę Database i wybierz z menu głównego Object/New. Pojawi się nowe okienko New Database Alias (rysunek poniżej).



W oknie tym wybierz sterownik do Oracle i kliknij przycisk OK. Pojawi się ponownie okno BDE Administrator, w którym do zakładki Database dodana zostanie nowa pozycja ORACLE1. Zmień jej nazwę na ORACLEBDE (rysunek poniżej).



Aby stworzyć nowy sterownik BDE, musisz jeszcze zmienić dwie właściwości znajdujące się po prawej stronie okna w BDE Administratorze:
  • w polu Server Name wpisz orcl;
  • w polu User Name wpisz nazwę użytkownika bazy danych.

Na koniec naciśnij przycisk Apply (niebieska strzałka) w celu zapisania nowego połączenia BDE.
Przetestujmy teraz stworzone przed chwilą połączenia BDE.
  1. Otwórz Delphi i dodaj do formularza komponent Database1 z palety BDE.
  2. Ustaw właściwość AliasName komponentu Database1 na stworzone przed momentem połączenie ORACLEBDE.
  3. We właściwości DatabaseName komponentu Database1 wpisz nazwę bazy danych orcl.
  4. Zmień właściwość Connected komponentu Database1 na True. Pojawi się okno, w którym będziesz musiał podać hasło dostępu do bazy danych. Jeśli je podasz i właściwość Connected zostanie zmieniona na True, oznacza to, że połączenie się powiodło

Przyjrzyjmy się jeszcze właściwościom i zdarzeniom komponentu Database (tabele poniżej)



Tabela 1. Właściwości komponentu Database

Właściwość
Opis</td></tr> </td></tr> AliasName</td>Specyfikuje alias BDE wykorzystywany przez ten komponent.</td></tr> Connected</td>Specyfikuje, czy połączenie z bazą danych jest aktywne. Ustawienie właściwości na True uaktywnia połączenie, natomiast na False powoduje odłączenie od bazy danych.</td></tr> DatabaseName</td>Specyfikuje nazwę bazy danych związaną z komponentem Database.</td></tr> DriverName</td>Specyfikuje nazwę sterownika BDE wykorzystywanego przez DataBase.</td></tr> Exclusive</td>Specyfikuje, czy dana aplikacja ma wyłączność na połączenie z bazą danych (wartość domyślna False).</td></tr> HandleShared</td>Specyfikuje, czy uchwyt do bazy danych może być współdzielony (wartość domyślna False).</td></tr> KeepConnection</td>Specyfikuje, czy aplikacja utrzymuje połączenie z bazą danych, nawet jeśli nie jest otwarty żaden zbiór danych.</td></tr> LoginPrompt</td>Specyfikuje, czy przed otwarciem nowego połączenia będzie wyświetlane okno dialogowe służące do logowania (wartość domyślna True).</td></tr> Name</td>Specyfikuje nazwę komponentu.</td></tr> Params</td>Specyfikuje zestaw parametrów dla połączenia z bazą danych, np. nazwę użytkownika i hasło.</td></tr> ReadOnly</td>Specyfikuje, czy połączenie z bazą danych umożliwia przeprowadzenie tylko operacji odczytu (wartość domyślna False).</td></tr> SessionName</td>Specyfikuje nazwę sesji wykorzystywanej przez ten komponent.</td></tr> Tag</td>Dodatkowa właściwość komponentu. Pozwala na wprowadzenie dowolnej wartości. Może zastępować zmienną w kodzie. Właściwość ta znajduje się w większości komponentów.</td></tr> TransIsolatio</td>Specyfikuje poziom izolacji transakcji zarządzanych przez BDE.</td></tr> </td></tr> </table>



Tabela 2. Zdarzenia komponentu Database
</td></tr> Zdarzenie</td>Opis</td></tr> </td></tr> AfterConnect</td>Zdarzenie obsługiwane po połączeniu się aplikacji z bazą danych.</td></tr> AfterDisconnect</td>Zdarzenie obsługiwane po rozłączeniu się z bazą danych.</td></tr> BeforeConnect</td>Zdarzenie obsługiwane tuż przed połączeniem się aplikacji z bazą danych.</td></tr> BeforeDisconnect</td>Zdarzenie obsługiwane tuż przed rozłączeniem się aplikacji z bazą danych.</td></tr> OnLogin</td>Zdarzenie obsługiwane w momencie podłączania się do bazy danych. W parametrze LoginParams przekazywane są nazwa użytkownika (user_name) i hasło (password), pobrane z właściwości Params komponentu. W zdarzeniu tym można umieścić obsługę własnego okna logowania (wtedy właściwość LoginPrompt musi być ustawiona na True).</td></tr> </td></tr> </table>

Komponent Table



Najprostszym sposobem dostępu do danych przechowywanych w bazie danych orcl jest użycie komponentu Table. Komponent ten oznacza po prostu tabelę bazy danych. Przyjrzyjmy się najpierw właściwościom udostępnianym przez komponent (tabela ponizej). Zdarzenia komponentu Table są bardzo podobne do udostępnianych przez komponent Query i zostały omówione w jednym z następnych podrozdziałów książki i nie chce mi się ich tu przepisywać :)


Tabela 3. Właściwści komponentu Table
</td></tr> Właściwość</td>Opis</td></tr> </td></tr> Active</td>Specyfikuje, czy zbiór danych (DataSet) jest otwarty.</td></tr> AutoCalcFields</td>Specyfikuje, kiedy zdarzenie OnCalcFields jest uruchamiane i kiedy wartości pól typu Lookup są obliczane.</td></tr> AutoRefresh</td>Specyfikuje, czy wartości pól przechowywane na serwerze są automatycznie odświeżane (wartość domyślna False).</td></tr> CashedUpdates</td>Specyfikuje, czy możliwe jest stosowanie trybu Cashed Updates (buforowane uaktualnienie). Jeśli ten tryb pracy jest włączony (wartość ustawiona jest ustawiona na True), to wszystkie operacje wykonywane na zbiorze danych odbywają się w buforze po stronie aplikacji klienta. Po zakończeniu pracy wszystkie zmiany mogą zostać wysłane na serwer w ramach jednej transakcji. Wartość domyślna False.</td></tr> Constraints</td>Opis więzów integralności na poziomie rekordu. Tutaj można utworzyć więzy bazujące na wartości kilku atrybutów. W razie wystąpienia potrzeby wykorzystania więzów na poziomie pojedynczej kolumny, należy je stworzyć w odpowiednim komponencie klasy TField.</td></tr> DatabaseName</td>Specyfikuje nazwę bazy danych związaną z tym komponentem.</td></tr> DefaultIndex</td>Specyfikuje, czy dane w tabeli mają być uporządkowane według domyślnego indeksu, czyli według klucza głównego (wartość domyślna True).</td></tr> Exclusive</td>Specyfikuje, czy dana aplikacja ma wyłączność na połączenie z bazą danych (wartość domyślna False).</td></tr> FieldDefs</td>Specyfikuje listę definicji atrybutów w zbiorze danych.</td></tr> Filter</td>Właściwość używana w celu nałożenia filtru na zbiór danych.</td></tr> Filtered</td>Specyfikuje, czy filtr jest aktywny. Wartość domyślna to False.</td></tr> FilterOptions</td>Specyfikuje dodatkowe opcje związane z filtrami.</td></tr> IndexDefs</td>Przechowuje informacje o definicjach indeksów tabeli w formie tablicy.</td></tr> IndexFieldNames</td>Specyfikuje kolumny, które są używane jako indeks tabeli. Kolumny są rozdzielane średnikami. Ustawienie tej właściwości czyści właściwość IndexName.</td></tr> IndexFiles</td>Pozwala na odczyt lub ustawienie pól wykorzystywanych w kluczu.</td></tr> IndexName</td>Określa indeks wykorzystywany w tabeli. Jeśli właściwość nie jest zdefiniowana, to do określenia sortowania wykorzystywany jest klucz główny.</td></tr> MasterFields</td>Specyfikuje jeden lub więcej atrybutów w tabeli nadrzędnej, które są połączone z atrybutami w danej tabeli, w celu ustalenia związku master-detail między tabelami. Właściwość tę należy ustawić po wypełnieniu właściwości MasterSource.</td></tr> MasterSource</td>Specyfikuje nazwę zbioru danych, który będzie traktowany jako tabela nadrzędna dla danej tabeli.</td></tr> Name</td>Specyfikuje nazwę komponentu. </td></tr> ObjectView</td>Specyfikuje, czy pola są przechowywane w sposób hierarchiczny czy sekwencyjny.</td></tr> ReadOnly</td>Specyfikuje, czy tabela będzie dostępna w trybie tylko do odczytu (wartość domyślna False).</td></tr> SessionName</td>Specyfikuje nazwę sesji wykorzystywanej przez ten komponent.</td></tr> StoreDefs</td>Ustawienie tej właściwości na True powoduje, że definicje atrybutów (właściwość FieldDefs) i indeksów (właściwość IndexDefs), wypełnione przy projektowaniu aplikacji, mają zostać zapisane razem z modułem lub formularzem. Pozwoli to na tworzenie tabeli w bazie danych poprzez wywołanie metody CreateTable.</td></tr> TableName</td>Specyfikuje nazwę tabeli w bazie danych, która będzie reprezentowana przez komponent Table.</td></tr> TableType</td>Specyfikuje strukturę tabeli z bazy danych reprezentowanej przez dany komponent.</td></tr> Tag</td>Dodatkowa właściwość komponentu. Pozwala na wprowadzenie dowolnej wartości. Może zastępować zmienną w kodzie. Właściwość ta znajduje się w większości komponentów w Delphi.</td></tr> UpdateMode</td>Specyfikuje, w jaki sposób BDE odnajduje rekordy przy operacjach typu UPDATE.</td></tr> UpdateObject</td>Specyfikuje komponent typu UpdateObject, pozwalający na aktualizację danych zwracanych jako tylko do odczytu lub w trybie Cached Updates.</td></tr> </td></tr> </table>

Ok, zastosujmy w końcu komponent Table w praktyce. Napiszmy program, który wyświetli dane z tabeli Kategorie oraz pozwoli na ich modyfikację.

Umieść na formularzu komponenty Database1, Table1 z palety BDE, DataSource1 z karty Data Access i DBGrid1 z karty Data Control.
Ustaw właściwość AliasName komponentu Database1 na stworzone przed momentem połączenie ORACLEBDE.
We właściwości DatabaseName komponentu Database1 wpisz nazwę bazy danych orcl.
Kliknij dwa razy właściwość Params i w oknie Value List Editor (rysunek poniżej) wpisz dwa parametry: USER NAME i PASSWORD oraz odpowiadające im wartości (nazwę użytkownika i hasło). Parametry te będą potrzebne po to, by podczas próby łączenia się z bazą danych program nie pytał o login i hasło użytkownika.



Zmień właściwość LoginPrompt komponentu Database1 na False. Od tego momentu nie będzie pojawiało się okno z prośbą o podanie loginu i hasła.
Zmień właściwość Connected komponentu Database1 na True.
Ustaw właściwość DatabaseName komponentu Table1 na orcl.
Ustaw właściwość TableName komponentu Table1 na tabelę Kategorie.
Ustaw właściwość DataSet komponentu DataSource1 na Table1.
Ustaw właściwość DataSource komponentu DBGrid1 na DataSource1. Dwa ostatnie kroki wykonuje się w celu wyświetlenia danych pobranych z bazy danych na komponencie DBGrid1.
Na koniec zmień właściwość Active komponentu Table1 na True. Od tego momentu w komponencie DBGrid1 wyświetlone będą dane z tabeli Kategorie. Możesz zapisać i uruchomić projekt (rysunek poniżej).
</ol>



OK, program wyświetla dane. Ale jak dodać nowe wpisy lub modyfikować istniejące? Żeby zmodyfikować wpis, wystarczy ustawić kursor w odpowiednim polu siatki (po prostu kliknij myszą interesujące Cię pole) i poprawić istniejący tam wpis. Natomiast żeby dodać nowy wpis, trzeba ustawić kursor w dowolnym miejscu, a następnie za pomocą klawiszy strzałek przejść na koniec tabeli. Gdy pojawią się puste pola, należy wpisać w nie nowe wartości. Oczywiście w kolumnie Id_kategorii nie można wpisywać wartości, które już istnieją, ponieważ atrybut Id_kategorii jest kluczem głównym tabeli Kategorie i jego wartości muszą być niepowtarzalne. W kolumnie Id_kategorii można wpisywać tylko wartości numeryczne. W przeciwnym wypadku program zwróci komunikat o błędzie. W następnych podrozdziałach napiszemy, jak stworzyć program, w którym użytkownik nie będzie mógł ingerować w wartości atrybutu będącego kluczem głównym tabeli.

Specjalne cechy komponentu Table



Komponent Table posiada specjalne cechy, których nie mają inne komponenty zbioru danych, np. Query. Otóż w komponencie tym dostępne są specjalne metody wyszukiwania, takie jak: FindKey, FindNearest, GotoKey, GotoNearest. My przyjrzymy się bliżej dwóm pierwszym metodom. Odpowiedzialne są one za wyszukiwanie przybliżonych wartości (FindNearest) oraz za wyszukiwanie dokładne (FindKey). Metody te wyszukują tylko po atrybutach będących indeksami lokalnymi. Natomiast indeksy lokalne można założyć tylko na atrybutach będących kluczem głównym tabeli.


Zmodyfikujmy przykład z poprzedniego podrozdziału tak, aby można było wyszukiwać książki na podstawie ISBN.


Otwórz projekt z poprzedniego podrozdziału.
Zmień właściwość TableName komponentu Table1 tak, aby wskazywała na tabelę Ksiazki.
Zmień właściwość Active komponentu Table1 na True w celu wyświetlenia wszystkich rekordów.
Ustaw indeks na atrybucie ISBN. W tym celu ustaw właściwość IndexFieldNames komponentu Table1 na ISBN.
Dodaj do formularza dwa komponenty Edit i dwa przyciski (Button).
Zmień tekst wyświetlany na przyciskach (właściwość Caption) na Wyszukaj książki o podobnym ISBN i Wyszukaj książki o identycznym ISBN.
Oprogramuj zdarzenia onClick przycisków:
</ol>

procedure TForm1.Button1Click(Sender: TObject);
begin
    Table1.FindNearest([Edit1.Text]);//wyszukaj książki o podobnym ISBN
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
    //wyszukaj książki o identycznym ISBN, a jeśli takich nie ma, to wyświetl komunikat
    if Table1.FindKey([Edit2.Text])=false then
       showmessage('Książka o takim ISBN nie istnieje w bazie danych');
end;


Parametrami obu metod są tablice stałych. Każdy element tablicy odpowiada indeksowanemu atrybutowi. W przykładzie jest jeden indeks na atrybucie ISBN, dlatego wyszukiwanie odbywa się tylko po tym jednym atrybucie.
Rysunek poniżej pokazuje program podczas działania. Po wpisaniu w polach edycyjnych wartości takich, jak na rysunku, wskaźnik (czarny trójkąt) zostanie ustawiony na drugim rekordzie. Widać, że metoda FindNearest znajduje odpowiedni rekord, pomimo iż wartość ISBN różni się dwoma cyframi.



Metoda Locate



Metody FindKey i FindNearest szukały rekordy tylko po atrybucie, który był indeksem. Metoda Locate pozwala wyszukiwać rekordy według dowolnego atrybutu tabeli. Posiada ona trzy parametry:

pierwszym jest nazwa lub nazwy atrybutów, według których będzie prowadzone wyszukiwanie;
drugim jest tablica wartości poszukiwanych;
trzecim są opcje metody Locate. Dostępne są dwie opcje:

loCaseInsensitive ? atrybuty i wartości poszukiwane są porównywane bez uwzględnienia wielkości liter,
loPartialKey ? jeśli podane parametry wyszukiwania spełniają przynajmniej w części wartości występujące w polach, to metoda zwraca wartość True.
</ul>
</ul>

Metoda Locate jako rezultat zwraca wartość typu Boolean. Jeśli znaleziony został rekord odpowiadający poszukiwanej wartości, to wynikiem działania metody jest wartość True. Zaprezentujmy wykorzystanie metody Locate na przykładzie.

Wykorzystaj przykład z podrozdziału ?Table?. Zmień właściwość TableName komponentu Table1 tak, aby wskazywała na tabelę Ksiazki.
Zmień właściwość Active komponentu Table1 na True w celu wyświetlenia wszystkich rekordów.
Dodaj do formularza komponenty Edit1 i Button1. W kontrolce Edit1 będziemy wpisywać tytuł poszukiwanej książki.
Zmień właściwość Caption komponentu Button1 na Szukaj książek o tytule.
Oprogramuj zdarzenie onClick komponentu Button1.
</ol>

procedure TForm1.Button1Click(Sender: TObject);
begin
    //jeśli wpisano tytuł w kontrolce Edit1
    if Edit1.text<>'' then
        //spróbuj znaleźć książkę o podanym tytule
       if Table1.Locate('Tytul',VarArrayOf([Edit1.Text]),[loPartialKey])=False
       then
          //jeśli nie znaleziono książki o podanym tytule, to wyświetl komunikat
          showmessage('Nie znaleziono książki zawierającej taki tytuł');
end;


Jeśli chcesz wyszukiwać rekordy według dwóch atrybutów, to musisz nieco zmodyfikować parametry metody Locate:

Table1.Locate('Tytul;ISBN',VarArrayOf([Edit1.Text,Edit2.Text]),[loPartialKey]);


Podsumowanie BDE



To tyle jesli chodzi o zagadnienia związane ze starą już i powoli odchodzącą do lamusa, lecz ciągle pozwalającą na efektywne proramowanie baz danych technologią - BDE. Dużo więcej informacji znajdziecie w książce.


ADO




Wstęp do ADO



ADO (ActiveX Data Objects) jest kolejną technologią dostępu do baz danych, za pomocą której możemy programować bazę danych Oracle 10g. Wyodrębniła się ona z technologii OLE.DB i obecnie jest częścią MDAC (Microsoft Data Access Components). Więcej informacji na temat MDAC, OLE.DB i ADO znajdziecie na stronie internetowej Microsoftu www.microsoft.com.
W Delphi 7 znajduje się paleta komponentów nazwana ADO. Składa się ona z 6 komponentów:

ADOConnection ? odpowiedzialny za łączenie z bazą danych,
ADOCommand ? odpowiedzialny za wykonywanie instrukcji języka SQL,
ADODataSet ? przechowujący zbiór danych w postaci kolumn i wierszy, posiada wiele zastosowań,
ADOTable ? odpowiedzialny za wykonywanie operacji na tabeli,
ADOQuery ? odpowiedzialny za wykonywanie zapytań SQL,
ADOStoredProc ? odpowiedzialny za wykonywanie procedur składowanych,
RDSConnection ? odpowiedzialny za łączenie z usługą RDS (Remote Data Services).
</ul>

ADOConnection i łączenie z bazą



Komponent ADOConnection jest używany do osiągnięcia wielu celów m.in. do:

realizowania wspólnego połączenie z bazą danych,
dostosowania procedury logowania,
sterowania transakcjami,
wykonywania poleceń,
redukowania liczby połączeń w aplikacji.
</ul>

Jako pierwsza cecha tego komponentu została wymieniona realizacja połączenia z bazą danych. Jednak komponent ten nie posiada właściwości DatabaseName ani AliasName, proces nawiązywania połączenia wyglada inaczej niż w przypadku technologii BDE:

Tworzymy nowy projekt i umieszczamy na formularzu komponent ADOConnection1.
Definiujemy łańcuch połączenia ConnectionString. Możemy to zrobić na dwa sposoby:

poprzez dwukrotne kliknięcie komponentu ADOConnection1,
poprzez dwukrotne kliknięcie właściwości ConnectionString komponentu ADOConnection1 w Inspektorze Obiektów
</ul>



Domyślnie zaznaczona jest opcja Use Connection String. Drugą możliwością definiowania łańcucha połączeń jest wskazanie pliku DataLink (opcja Use Data Link File) - zostanie ona omówiona w dalszej części artykułu.
Po kliknięciu przycisku Build pojawi się okno Właściwości łącza danych zawierające cztery zakładki: Dostawca, Połączenie, Zaawansowane, Wszystkie.



Na zakładce Dostawca wybierz sterownik Oracle Provider for OLE DB.
Wciskając przycisk Dalej przecjdź do następnej zakładki.
Na zakładce Połączenie zdefiniuj:

źródło danych ? SID (unikalny identyfikator bazy danych Oracle). Został on wybrany podczas instalacji bazy danych Oracle 10g. W moim przypadku (standardowa instalacja) jest to orcl,
nazwę użytkownika bazy danych - czyli ja wpisałem artur,
hasło dostępu do bazy danych dla tego użytkownika - u mnie też artur;
dodatkowo można zaznaczyć dwie opcje: Puste hasło (w przypadku gdy użytkownik bazy danych nie ma zdefiniowanego hasła w bazie danych), Zezwalaj na zapisywanie hasła (jeśli nie chcemy podawać hasła przy każdej próbie połączenia z bazą danych Oracle 10g, opcja ta musi być zaznaczona).
</ul>



Aby sprawdzić poprawność konfiguracji, wciśnij przycisk Testuj połączenie. W przypadku poprawnej konfiguracji otrzymasz komunikat Połączenie testowe powiodło się.
Zapisz łańcuch połączeń, klikając przycisk OK na dwóch kolejnych oknach

</ol>



Przedstawione przed chwilą zastosowanie łańcucha połączeń (ConnectionString) jest prostą metodą łączenia się z bazą danych. Posiada jednak wadę. Łańcuch połączeń jest wkompilowywany w plik exe aplikacji. Wymusza to statyczną lokalizację bazy danych. W przypadku zmiany np. nazwy użytkownika lub bazy danych należy stworzyć łańcuch połączeń od nowa, a następnie ponownie skompilować całą aplikację. Rozwiązaniem tego problemu jest zastosowanie plików łączenia danych (tzw. Data Link).
Plik Data Link to rodzaj pliku konfiguracyjnego, który posiada rozszerzenie udl. Plik ten możesz przygotować w następujący sposób:


Utwórz plik tekstowy, np. za pomocą Notatnika, i zmień jego rozszerzenie na udl.
Dwukrotnie kliknij nowo utworzony plik.
Pojawi się znane już okno konfiguracji połączenia z bazą danych.
Wykonaj te same czynności, co w przypadku definiowania łańcucha połączeń.
Jeśli zaznaczysz opcję Zezwalaj na zapisywanie hasła, zostanie wyświetlone okno z ostrzeżeniem, że hasło zostanie zapisane jawnym tekstem w pliku udl
Jeśli chcesz zapisać hasło w pliku udl to kliknij Tak
</ol>

Przykładowa zawartość pliku udl:
[oledb]
; Everything after this line is an OLE DB initstring
Provider=OraOLEDB.Oracle.1;Password=artur;Persist Security Info=True;User ID=artur;Data Source=orcl


Uwaga: Ręczne wpisanie (skopiowanie) ustawień spowoduje błąd podczas próby połączenia się z bazą danych - aby plik Data Link był poprawny należy go wykonać według powyższych punktów.

Oki teraz możemy wykorzystać plik Data Link do połączenie z bazą danych, aby to zrobić należy:

Umieścić komponent ADOConnection1 na formularzu i dwukrotnie go kliknąć.
W nowo otwartym oknie (znanym już dlatego nie zamieszczam rysunku) zaznaczyć opcję Use Data Link File.
Kliknąć przycisk Browse i wskazać plik udl, a następnie kliknąć przycisk Otwórz.
Dokonany wybór zatwierdzić przyciskiem OK.
</ol>

Przykładowa aplikacja ADO



Ok, wiesz już, jak połączyć się z bazą oracle 10g za pomocą ADO, to teraz napiszę aplikację pokazującą zastosowanie technologii ADO - a konkretnie wykorzystanie komponentu ADOCommand. Wybaczcie, że nie poruszę większej ilości zagadnień związanych z ADO, ale t jest tylko artykuł:) - więcej informacji jak zwykle znajdziecie w przedstawionej wyżej książce :).

Ok, zaczynamy. ADOCommand służy do wykonywania różnego rodzaju zapytań SQL
Do definiowania zapytań służy właściwość CommandText. Podwójne kliknięcie tej właściwości spowoduje otworzenie CommandText Editora ? okna, w którym można podglądać tabele i ich atrybuty, a także formułować treść zapytania SQL.
Ja napiszę prosty program, który będzie dodawał nowe wpisy do tabeli Kategorie (do bazy danych zaprojektowanej na początku artykułu). Tabela ta posiada dwa atrybuty: Id_kategorii i Nazwa (liczbę i nazwy atrybutów łatwo sprawdzić, wydając polecenie DESC NAZWA_TABELI w iSQL*Plus). Aby stworzyć aplikację, musisz wykonać kilka kroków:

Umieść na formartce dwa komponenty Label i zmień ich właściwości Caption na ID i Nazwa kategorii.
Umieść na formartce dwa komponenty Edit do wpisywania danych i usuń zawartość właściwości Text.
Umieść na formartce komponent ADOConnection1 i skonfiguruj łańcuch połączeń, wskazując bazę danych orcl.
Umieść na formartce komponent ADOCommand1 i powiąż go z kompponentem ADOConnection1 - ustaw właściwość Connection komponentu ADOCommand1 na ADOConnection1.
Umieść na formartce komponent Button1 i zmień jego właściwość Caption na Dodaj nową kategorię.
Oprogramuj zdarzenie onClick komponentu Button1
</ol>

procedure TForm1.Button1Click(Sender: TObject);
begin
    //zapytanie Insert wstawiające dane do tabeli
    ADOCommand1.CommandText:='INSERT INTO Kategorie VAlUES ('+Edit1.Text+','''+Edit2.Text+''')';
    //klauzula try przechwytująca ewentualne wyjątki
    try
       ADOCommand1.Execute; //wykonanie polecenia INSER
    except
       //poinformowanie użytkownika o wyjątku
       Showmessage('Wystąpił błąd. Może to być spowodowane niewypełnieniem pola Edit lub błędem związanym z kluczem głównym tabeli Kategorie');
    end;
end;




W powyższym przykładzie zapytanie zostało zbudowane za pomocą łączenia łańcuchów (stringów) - istnieje inna metoda wykonywania zapytań za pomocą parametrów. Następnie tak zbudowane zapytanie jest wykonywane (ADOCommand1.Execute). Podczas wykonywania zapytania mogą wystąpić wyjątki związane z kilkoma sytuacjami: użytkownik nie wypełnił dowolnego z pól Edit, użytkownik podał nieprawidłową wartość dla pola ID. Dlatego BARDZO ŹŁĄ praktyką jest zostawianie użytkownikowi możliwości ingerowania w klucz główny tabeli. Bardzo eleganckim i efektywnym rozwiązaniem jest użycie sekwencji - dzięki temu to serwer oracle będzie się martwił, aby atrybut Id mial odpowiednią, nie powtarzającą się wartość. Ok, to zmieńmy naszą aplikację. Najpierw stwórz w Oracle sekwencję - otwórz iSQLPlusa i wykonaj kod:

create sequence klucz_glowny_tabeli_kategorie
increment by 1
start with 1


Pierwsza linia zawiera nazwę sekwencji, druga wartość o jaką sekwencja będzie zwiększana a trzecia początkową wartość sekwencji - dzięki temu pierwszy wstawiony rekord będzie miał wartość ID 1. Oczywiście należy pamiętać o tym, że jeśli w tabeli są już jakieś rekordy to wartość początkową sekwencji należy ustawić na inną (większą) wartość. Ok, teraz należy zmienić troche stworzoną aplikację.

Usuń z formularza dwa komponenty Label1 i Edit1 ? nie będą już potrzebne.
Zmień kod zdarzenia onClick komponentu Button1 na poniższy
</ol>

procedure TForm1.Button1Click(Sender: TObject);
begin
    ADOCommand1.CommandText:='INSERT INTO Kategorie VAlUES  (klucz_glowny_tabeli_kategorie.nextval ,'''+Edit2.Text+''')';
    try
       ADOCommand1.Execute;
    except
       Showmessage('Wpisz nazwę kategorii');
    end;
end;


Wartości klucza głównego generowane są teraz przez sekwencję. Polecenie

klucz_glowny_tabeli_kategorie.nextval


powoduje zwiększenie o 1 ostatniej wartości klucza głównego tabeli Kategorie


Zapytania z parametrami



W powyższym przykładzie zapytania INSERT tworzone były za pomocą sklejania (konkatenacji) łańcuchów tekstowych. Istnieje inna, bardziej elegancka, metoda budowania zapytań ? z użyciem parametrów.


Zbuduj interfejs użytkownika taki jak w poprzednim nie poprawionym przykładzie.
Dwukrotnie kliknij właściwość Parameters komponentu ADOCommand1 ? otworzy się nowe okno Editing ADOCommand1.Parameters



Dodaj dwa parametry, klikając ikonę ADD New lub wciskając klawisz Insert.
Nadaj nazwy parametrom ? w tym celu zaznacz parametr w oknie Editing ADOCommand1.Parameters i uzupełnij właściwość Name w Inspektorze Obiektów.
Oprogramuj zdarzenie onClick komponentu Button1.
</ol>

procedure TForm1.Button1Click(Sender: TObject);
begin
    //odczytanie wartości parametrów
    ADOCommand1.Parameters.ParamByName('ID').Value:=Edit1.Text;
    ADOCommand1.Parameters.ParamByName('NAZWA').Value:=Edit2.Text;
    //tworzenie zapytania
    ADOCommand1.CommandText:='INSERT INTO Kategorie (ID_KATEGORII, NAZWA) VALUES (:ID, :NAZWA)';
    try
       ADOCommand1.Execute; //próba wykonania zapytania
    Except
       //wyświetl komunikat w przypadku wystąpienia błędu
       Showmessage('Błąd!! ? Wpisz poprawną wartość klucza głównego lub uzupełnij nazwę kategorii');
    end;
end;


Jak widać, parametry zapytania poprzedzane są znakiem dwukropka. W przypadku gdy w zapytaniu uczestniczą wszystkie atrybuty tabeli, nie ma konieczności wymieniania ich po nazwie tabeli w poleceniu INSERT (ale jest to dobrą praktyką). Konieczność wymienienia atrybutów tabeli istnieje wówczas, gdy w poleceniu INSERT nie uczestniczą wszystkie atrybuty tabeli. Oczywiście atrybutami nieuczestniczącymi w zapytaniu INSERT mogą być tylko te, które nie posiadają predykatu NOT NULL nadanego w Oracle.
Wartość parametru można ustawić za pomocą polecenia:
ADOCommand1.Parameters.ParamByName('Nazwa paramteru').Value := Wartość;


Oczywiście znów możesz zmodyfikować program, by użytkownik nie musiał wpisywać wartości ID.

Podsumowanie technologii ADO



ADO to nowoczesna (teraz jest już ADO.NET) technologia, która jest dość szybka i efektywna. Oczywiście w artykule poruszyłem tylko małą cześć zagadnień związanych z ADO. Dużo więcej znajduje się w książce "Oracle 10g i delphi. Programownaie baz danych".

dbExpress



Parę słów o dbExpress



dbExpress to stosunkowo nowa technologia dostępu do baz danych, polecana przez firmę Borland do obsługi serwerów baz danych. Za pomocą tej technologii możemy tworzyć aplikacje pracujące w środowisku Windows i Linux (oprócz dbExpress możliwość taką udostępnia jeszcze technologia ODAC, omówiona w dalszej części artykułu). dbExpress dostępna jest w Delphi i Kyliksie. Jest to szybka i elastyczna technologia - dużo szybsza niż BDE czy ODBC. Co powoduje, że technologia ta jest szybsza od innych? W dbExpress wyniki zapytań przechowywane są w kursorach jednokierunkowych. Tego typu kursory pozwalają przejść z dowolnego rekordu do następnego, ale za to nie umożliwiają powrotu do poprzedniego. Cecha ta powoduje, że nie można używać komponentów DBGrid (przynajmniej bez specjalnych sztuczek) do prezentacji danych. Jednak takie podejście sprawia, że wszystkie operacje związane z przetwarzaniem danych wykonywane są znacznie szybciej niż w innych technologiach.

Paleta komponentów dbExpress składa się z kilku kontrolek:

SQLConnection ? komponent służący do nawiązywania połączenia z bazą danych,
SQLDataSet ? komponent zbioru danych, może być używany zamiast SQLQuery, SQLTable lub SQLStoredProc,
SQLQuery ? komponent służący do przechowywania zbioru danych i wykonywania zapytań SQL,
SQLStoredProc ? komponent służący do wykonywania procedur składowanych na serwerze baz danych,
SQLTable ? komponent służący do przechowywania zbioru danych pochodzącego z jednej tabeli,
SQLMonitor ? komponent służący do monitorowania komend wysyłanych pomiędzy aplikacją a serwerem Oracle 10g,
SimpleDataSet ? komponent zbioru danych.
</ul>


Nawiązywanie połączenia z bazą danych



Żeby połączyć się z bazą danych, musisz otworzyć Delphi i na formatce umieścić komponent SQLConnection1. Następnie należy dwukrotnie kliknąć lewym klawiszem myszy ten komponent lub kliknąć prawym klawiszem myszy i z menu podręcznego wybrać opcję Edit Connection Properties. Ukaże się nowe okno.



Zeby połączyć się z oracle w polu Connection Name musisz wybrać opcję OracleConnection, a następnie uzupełnić kilka właściwości w polu Connection Settings:

DataBase ustaw na orcl jeśli taką masz nazwę bazy danych (standardowa instalacja),
UserName ? tu wpisz swoją nazwę użytkownika (u mnie artur),
Password ? w polu tym wpisz hasło użytkownika bazy danych (u mnie artur).
</ul>
Na koniec możesz przetestować połączenie z bazą danych naciskając przycisk Test Connection (czwarty od lewej strony w oknie przedstawionym na rysunku powyżej). Jeśli otrzymasz komunikat Succesfully connected, będzie to oznaczać, że połączenie z bazą danych Oracle powiodło się. Na koniec zatwierdź zmiany poprzez naciśnięcie przycisku OK.

SQLQuery i wyświetlanie danych



SQLQuery to komponent będący odpowiednikiem komponentów Query i ADOQuery. Służy do wykonywania zapytań SQL i przechowywania wynikowego zbioru danych (jeśli taki istnieje). Komponent SQLQuery różni się od odpowiedników z technologii BDE i ADO jedną ważną cechą ? dane możemy przeglądać tylko do przodu. Dlatego wyświetlanie danych za pomocą technologii dbExpress jest trochę inne niż w technologiach BDE czy ADO. Napiszmy program w oparciu o komponent SQLQuery, który wyświetli dane z tabeli Kategorie.


Umieść na formularzu komponent SQLConnection1 i skonfiguruj połączenie z bazą danych oraz zmień właściwość Connected na True.
Umieść na formularzu komponent SQLQuery1 i ustaw jego właściwość SQLConnection na SQLConnection1.
Kliknij dwukrotnie właściwość SQL komponentu SQLQuery1 i w oknie String List Editor wpisz treść zapytania SQL, które wyświetli dane z tabeli Kategorie:
SELECT * FROM Ksiazki

Umieść na formularzu komponent ListView1 z palety Win32. To na nim będziemy wyświetlać dane pobrane z tabeli Kategorie.
Zmień kilka właściwości komponentu ListView1, żeby przystosować go do wyświetlania danych z bazy danych:

właściwość Align ustaw na alTop, żeby komponent był dopasowany do formularza,
właściwość ViewStyle ustaw na vsReport, żeby móc wyświetlać dane w postaci kolumn i wierszy,
właściwość ReadOnly zmień na True, żeby nie można było zmieniać danych wyświetlanych w komponencie ListView1,
właściwość RowSelect ustaw na True, żeby wybrany wiersz tabeli był w całości zaznaczany,
właściwość GridLines ustaw na True, żeby widoczna była siatka oddzielająca wiersze i kolumny,
właściwość ShowColumnHeader ustaw na True, żeby widoczne były nagłówki kolumn.
</ul>
Aby dodać nagłówki kolumn, kliknij prawym klawiszem myszy komponent ListView1 i z menu podręcznego wybierz opcję Column Editor. Za pomocą przycisku Add New dodaj kolumny i zmień ich właściwość Caption według własnego uznania. Oczywiście za pomocą przycisków Move Selected Up, Move Selected Down możesz zmieniać kolejność kolumn, a za pomocą przycisku Delete Seleted ? usuwać niepotrzebne kolumny.



Dodaj do formularza przycisk i zmień jego właściwość Caption na Wyświetl dane.
Oprogramuj zdarzenie onClick przycisku Button1 Naciśnięcie tego przycisku będzie powodowało wyświetlenie danych pobranych z bazy.
</ol>

procedure TForm1.Button1Click(Sender: TObject);
var
   lista:TListItem;  //zmienna, która posłuży do dodawania wierszy
begin
   ListView1.Items.Clear; //wyczyść zawartość ListView1
   SQLQuery1.Active:=True; //wykonaj zapytanie SQL
   // dopóki są dane w zbiorze danych 
   while not SQLQuery1.Eof do
   begin
       lista:=ListView1.Items.Add; //stwórz nowy wiersz
       // do wiersza wstaw wartość przechowywaną przez pole ID ... 
       lista.Caption:=SQLQuery1.FieldValues['ID'];
       // ... a następnie wstaw wartości z kolumny nazwa
       try
          lista.SubItems.Add(SQLQuery1.FieldValues['NAZWA'])
       except
          lista.SubItems.Add('');
       end;
       SQLQuery1.Next; //przejdź do kolejnego rekordu zbioru danych
   end;
   SQLQuery1.Close; //zamknij komponent SQLQuery1
end;


Jak zapewne zauważyłeś, pierwszą kolumnę w komponencie ListView1 dodaje się poprzez wywołanie metody
Caption

natomiast następne poprzez wywołanie metody
SubItem.Add

Wypisywanie wartości pól, które mogą nie zawierać danych, powinno odbywać się w bloku
try 
... 
except
...
end;

tak jak to zaprezentowałem w przypadku danych z kolumny Nazwa (choć kolumna ta jest oznaczona jako Not Null - więc musi posiadać jakąś wartość - blok try ... except zastosowałem, żeby pokazać mechanizm).
Skompiluj projekt i uruchom go poza środowiskiem Delphi. Uruchamiając program z poziomu środowiska Delphi (jesli zawiera on blok try ... except), możesz otrzymać komunikat o błędzie, np. wtedy gdy jedno z pól nie będzie zawierało danych. Dzieje się tak dlatego, że Delphi wykrywa wyjątek jeszcze przed zadziałaniem bloku try ... except.


Edycja danych



Przed chwilą zaprezentowałem program, który pozwalał przeglądać dane, w tym zaś rozbuduję aplikację, by umożliwiała edycję danych: dodawanie, modyfikowanie i usuwanie rekordów. Zapytania będę budował na zasadzie sklejania łańcuchów tekstowych, jednak nic nie stoi na przeszkodzie, by zastosować parametry.

Otwórz projekt w poprzedniego podrozdziału i dodaj do niego komponent Label, pole edycyjne (Edit), trzy przyciski (Button). Komponent Label będzie wyświetlać napis "Nazwa". Pole edycyjne posłuży do dodawania nowych i aktualizacji istniejących wpisów. Za pomocą przycisków będziemy mogli dodać, zmodyfikować lub usunąć wpis. Proces dodawania rekordu będzie polegał na uzupełnieniu pola Nazwa i naciśnięciu przycisku Dodaj dane. Proces modyfikacji będzie polegał na wybraniu w komponencie ListView1 wiersza, który ma zostać zmieniony. Dane pobrane z tego wiersza zostaną wyświetlone w polu edycyjnym (Nazwa). Użytkownik będzie mógł nanieść poprawki i nacisnąć przycisk Modyfikuj dane. Po wykonaniu tych czynności dane zostaną zmodyfikowane. Proces usuwania danych będzie polegał na wskazaniu w komponencie ListView1 rekordu, który ma zostać usunięty, i wybraniu przycisku Usuń dane.
Zmień właściwości przed chwilą wstawionych komponentów, tak aby stworzyć interfejs użytkownika.
Stwórz zmienną globalną do zapamiętania wartości pola Id_kategorii klikniętego w Listview1.
zapamietaj_id:string; //zmienna przechowująca wartość ID_kategorii zaznaczonego wiersza

Procedura wyświetlajaca dane jest następująca:
procedure TForm1.Button1Click(Sender: TObject);
var
    lista:TListItem;
begin
    ListView1.Items.Clear;
    SQLQuery1.Active:=False;
    SQLQuery1.SQL.Clear;
    SQLQuery1.SQL.Add('SELECT * FROM Kategorie');
    SQLQuery1.Active:=True;
    while not SQLQuery1.Eof do
    begin
       lista:=ListView1.Items.Add;
       lista.Caption:=SQLQuery1.FieldValues['ID_KATEGORII'];
       try
          lista.SubItems.Add(SQLQuery1.FieldValues['NAZWA'])
       except
          lista.SubItems.Add('');
       end;
       SQLQuery1.Next;
    end;
    SQLQuery1.Close;
end;

Napisz odpowiedni kod służący do wstawiania nowych rekordów do tabeli Kategorie. Oprogramuj zdarzenie onClick komponentu Button2:
procedure TForm1.Button2Click(Sender: TObject);
var id,i:integer;
    nazwa:string;
begin
    if (Edit1.Text<>'') then
    begin
        nazwa:=''''+Edit1.Text+'''';
        SQLQuery1.Close;
        SQLQuery1.SQL.Clear;
        SQLQuery1.SQL.Add('INSERT INTO Kategorie VALUES (klucz_glowny_tabeli_kategorie.nextval,'+nazwa+')');
        try
           SQLQuery1.ExecSQL;
        except
           showmessage('Wystąpil bląd podczas wstawiania rekordu. Sprawdź, czy wszystkie pola są poprawnie wypelnione')
        end;
    end;
    Button1Click(Sender);
    Edit1.Text:='';
    SQLQuery1.Active:=False;
end;

Na początku i na końcu sczytywanych wartości pól typu łańcuchowego dodajemy apostrof (czynność ta jest konieczna podczas budowania zapytań poprzez łączenie łańcuchów).
Oprogramuj zdarzenie onSelectItem komponentu ListView1. Zdarzenie to zostanie wywołane w momencie zaznaczenia wiersza (kliknięcie lewym klawiszem myszy siatki ListView1) i spowoduje uzupełnienie pól edycji i zapamiętanie wartości ID_kategorii. Czynność ta będzie przydatna przy modyfikacji i usuwaniu rekordów z bazy danych.
procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
var
    i:integer;
begin
    zapamietaj_id:=''''+Item.Caption+'''';
    Edit1.Text:=Item.SubItems.Strings[0];
end;
 
procedure TForm1.Button4Click(Sender: TObject);
begin
    if zapamietaj_id<>'' then
    begin
       SQLQuery1.Active:=False;
       SQLQuery1.SQL.Clear;
       SQLQuery1.SQL.Add('DELETE FROM Kategorie WHERE ID_KATEGORII='+zapamietaj_id);
       SQLQuery1.ExecSQL;
    end
    else
       showmessage('Zaznacz rekord, który chcesz usunąc');
    Button1Click(Sender);
    zapamietaj_id:='';
    Edit1.Text:='';
end;

Odczytywanie wartości pierwszej kolumny wykonuje się za pomocą instrukcji
Item.Caption

natomiast odczytywanie wartości pozostałych kolumn jest możliwe poprzez wykonanie instrukcji
Item.SubItems.Strings[numer]

Numeracja jest oczywiście od zera. Tak więc, żeby odczytywać wartość pola z drugiej kolumny, należy posłużyć się instrukcją
Item.SubItems.Strings[0].

Kolejną czynnością jest oprogramowanie zdarzenia onClick komponentu Button3. W zdarzeniu tym będziemy zapisywać do bazy danych dokonane przez użytkownika zmiany, czyli będziemy wykonywać instrukcję UPDATE. Oczywiście użytkownik przed dokonaniem modyfikacji będzie musiał kliknąć wiersz, którego dane chce zmienić, czyli wykonana zostanie procedura przedstawiona w poprzednim punkcie Procedura modyfikacji danych niewiele różni się od procedury wstawiającej rekord do bazy danych, więc nie będę jej omawiał.
procedure TForm1.Button3Click(Sender: TObject);
var id,i:integer;
    nazwa:string;
begin
    if (zapamietaj_id<>'') then
      if (Edit1.Text<>'') then
      begin
          nazwa:=''''+Edit1.Text+'''';
          SQLQuery1.Active:=False;
          SQLQuery1.SQL.Clear;
          SQLQuery1.SQL.Add('UPDATE Kategorie SET NAZWA='+nazwa+' WHERE ID_KATEGORII='+zapamietaj_id);
          try
             SQLQuery1.ExecSQL;
          except
             showmessage('Wystąpil bląd podczas aktualizacji rekordu. Sprawdź, czy wszystkie pola są poprawnie wypelnione')
          end;
      end
      else
         showmessage('Pole Nazwa musi byc uzupelnione')
    else
      showmessage('Zaznacz wpis, który chcesz zmienic');
    Button1Click(Sender);
    zapamietaj_id:='';
    Edit1.Text:='';
end;

Na koniec pozostało nam oprogramowanie zdarzenia onClick komponentu Button4. Kliknięcie tego przycisku będzie powodowało usunięcie wskazanego w siatce ListView1 rekordu.
procedure TForm1.Button4Click(Sender: TObject);
begin
    if zapamietaj_id<>'' then
    begin
       SQLQuery1.Active:=False;
       SQLQuery1.SQL.Clear;
       SQLQuery1.SQL.Add('DELETE FROM Kategorie WHERE ID_KATEGORII='+zapamietaj_id);
       SQLQuery1.ExecSQL;
    end
    else
       showmessage('Zaznacz rekord, który chcesz usunąc');
    Button1Click(Sender);
    zapamietaj_id:='';
    Edit1.Text:='';
end;

</ol>

Możesz już skompilować i uruchomić program.



Listing całego programu został zaprezentowany poniżej (uruchamiałem go na Delphi 2005 więc mogą być drobne różnice w modułach dodanych po słowie uses w Delphi 7 - ale powinniście sobie poradzić)

unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DBXpress, FMTBcd, StdCtrls, ComCtrls, DB, SqlExpr, Spin;
 
type
  TForm1 = class(TForm)
    SQLConnection1: TSQLConnection;
    SQLQuery1: TSQLQuery;
    ListView1: TListView;
    Button1: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure Button4Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
  zapamietaj_id:string;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.Button1Click(Sender: TObject);
var
    lista:TListItem;
begin
    ListView1.Items.Clear;
    SQLQuery1.Active:=False;
    SQLQuery1.SQL.Clear;
    SQLQuery1.SQL.Add('SELECT * FROM Kategorie');
    SQLQuery1.Active:=True;
    while not SQLQuery1.Eof do
    begin
       lista:=ListView1.Items.Add;
       lista.Caption:=SQLQuery1.FieldValues['ID_KATEGORII'];
       try
          lista.SubItems.Add(SQLQuery1.FieldValues['NAZWA'])
       except
          lista.SubItems.Add('');
       end;
       SQLQuery1.Next;
    end;
    SQLQuery1.Close;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
var id,i:integer;
    nazwa:string;
begin
    if (Edit1.Text<>'') then
    begin
        nazwa:=''''+Edit1.Text+'''';
        SQLQuery1.Close;
        SQLQuery1.SQL.Clear;
        SQLQuery1.SQL.Add('INSERT INTO Kategorie VALUES (klucz_glowny_tabeli_kategorie.nextval,'+nazwa+')');
        try
           SQLQuery1.ExecSQL;
        except
           showmessage('Wystąpil bląd podczas wstawiania rekordu. Sprawdź, czy wszystkie pola są poprawnie wypelnione')
        end;
    end;
    Button1Click(Sender);
    Edit1.Text:='';
    SQLQuery1.Active:=False;
end;
 
procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
var
    i:integer;
begin
    zapamietaj_id:=''''+Item.Caption+'''';
    Edit1.Text:=Item.SubItems.Strings[0];
end;
 
procedure TForm1.Button4Click(Sender: TObject);
begin
    if zapamietaj_id<>'' then
    begin
       SQLQuery1.Active:=False;
       SQLQuery1.SQL.Clear;
       SQLQuery1.SQL.Add('DELETE FROM Kategorie WHERE ID_KATEGORII='+zapamietaj_id);
       SQLQuery1.ExecSQL;
    end
    else
       showmessage('Zaznacz rekord, który chcesz usunąc');
    Button1Click(Sender);
    zapamietaj_id:='';
    Edit1.Text:='';
end;
 
procedure TForm1.Button3Click(Sender: TObject);
var id,i:integer;
    nazwa:string;
begin
    if (zapamietaj_id<>'') then
      if (Edit1.Text<>'') then
      begin
          nazwa:=''''+Edit1.Text+'''';
          SQLQuery1.Active:=False;
          SQLQuery1.SQL.Clear;
          SQLQuery1.SQL.Add('UPDATE Kategorie SET NAZWA='+nazwa+' WHERE ID_KATEGORII='+zapamietaj_id);
          try
             SQLQuery1.ExecSQL;
          except
             showmessage('Wystąpil bląd podczas aktualizacji rekordu. Sprawdź, czy wszystkie pola są poprawnie wypelnione')
          end;
      end
      else
         showmessage('Pole Nazwa musi byc uzupelnione')
    else
      showmessage('Zaznacz wpis, który chcesz zmienic');
    Button1Click(Sender);
    zapamietaj_id:='';
    Edit1.Text:='';
end;
 
end.



Podsumowanie dbExpress



W części tej zostala zaprezentowana nowoczesna technologia dostępu do baz danych oracle 10g. Dużo więcej informacji znajdziecie w książce.

ODAC



Wstęp



ODAC (Oracle Data Access Components) to kolejna technologia pozwalająca programować bazy danych Oracle 10g, a zarazem pierwsza, która nie jest dostępna po zainstalowaniu Delphi. ODAC zostało stworzone jako alternatywa dla innych technologii, w szczególności dla BDE. ODAC wykorzystuje OCI (Oracle Call Interface), czyli pewnego rodzaju interfejs API (Application Programming Interface) do łączenia się z bazą.

Instalacja



ODAC to komponenty komercyjne, za które trzeba zapłacić. Jednak za darmo można pobrać wersję sześćdziesięciodniową. Ze strony producenta http://crlab.com/odac/ musisz ściągnąć dwa pliki, w przypadku Delphi 7 będą to odac7.exe i oratoolsadd.exe. Instalacja ODAC przebiega inaczej niż większości komponentów ? bardzo przypomina instalację programów w środowisku Windows. Należy po prostu dwa razy kliknąć na pliki exe i postępować zgodnie ze wskazówkami instalatora. Po zainstalowaniu komponentów ODAC trzeba jeszcze zainstalować narzędzia dodatkowe. Kliknij dwukrotnie plik oratoolsadd.exe, potwierdź zamiar instalacji i postępuj zgodnie z instrukcjami instalatora. Proces instalacji jest prosty, więc nikt nie powinien mieć problemów.

Paleta komponentów ODAC



Po zainstalowaniu komponentów ODAC w Delphi pojawi się nowa paleta komponentów, Oracle Access:

OraSession ? komponent służący do nawiązywania i kontrolowania połączenia z bazą danych,
OraQuery ? komponent służący do przechowywania zbioru rekordów i wykonywania zapytań SQL,
OraSmart (SmartQuery) ? alternatywa dla komponentu OraQuery,
OraSQL ? komponent służący do wykonywania instrukcji PL/SQL, procedur składowanych itp.,
OraTable ? odpowiednik komponentu Table w technologii BDE, służy do przechowywania zbioru danych i wykonywania operacji na jednej tabeli,
OraStoredProc ? komponent służący do wykonywania procedur i funkcji składowanych,
OraUpdateSQL ? odpowiednik komponentu UpdateSQL występującego w technologii BDE,
OraNestedTable ? komponent służący do obsługi tabel zagnieżdżonych,
OraDataSource ? odpowiednik komponentu DataSource,
OraScript ? komponent służy np. do wykonywania sekwencji,
OraPackage ? komponent służący do wykonywania pakietów PL/SQL,
OraAlert ? komponent pozwala na przekazywanie informacji pomiędzy sesjami,
OraLoader ? komponent pozwala na szybkie załadowanie danych do bazy danych,
OraSQLMonitor ? komponent służący do monitorowania zapytań SQL i zmian zachodzących w bazie danych, obsługiwanej przez technologię ODAC. Odpowiednik narzędzia SQL Monitor,
OraErrorHandler ? komponent służący do tłumaczenia komunikatów błędów,
BDESession ? pozwala integrować komponenty ODAC z aplikacjami stworzonymi w technologii BDE,
ConnectDialog ? służy do przechowywania nazwy użytkownika, hasła i nazwy serwera baz danych,
VirtualTable ? komponent służący do składowania zbioru danych w pamięci,
OraProvider
</ul>

Ja omówię tylko zastosowanie komponentu OraSQL - po więcej informacji odsyłam do książki.

Tworzenie połączenia z bazą danych



Żeby połączyć się z bazą danych stworzoną w środowisku Oracle 10g za pomocą komponentów ODAC, należy umieścić na formularzu komponent OraSession1. Następnie trzeba dwukrotnie kliknąć ten komponent lub kliknąć prawym klawisz myszy i z menu podręcznego wybrać opcję Session Editor. Ukaże się nowe okno, w którym należy wypełnić kilka pól.



W polu UserName należy wpisać nazwę użytkownika bazy danych (u mnie artur), w polu Password hasło tego użytkownika (u mnie artur). Z rozwijanej listy Server należy wybrać nazwę bazy danych ? w moim przypadku orcl. Pole Home pozwala na wybór serwera Oracle, z którym będziemy się łączyć - domyślnie ta opcja jest uzupełniona.

OraSQL



Komponent OraSQL pozwala na wykonywanie nie tylko zapytań SQL, ale również bloków języka PL/SQL. Zaprezentuję działanie komponentu OraSQL na przykładzie - Napiszę aplikację, która pozwoli na wykonywanie bloków PL/SQL i w niektórych wypadkach zaprezentuje wyniki.


Umieść na formatce komponent OraSession1 i skonfiguruj połączenie z bazą danych.
Umieść na formatce komponent OraSQL1 i sprawdź, czy jego właściwość Session jest ustawiona na OraSession1.
Umieść na formatce dwa komponenty Memo. Pierwszy będzie służył do wpisywania poleceń PL/SQL, drugi do prezentacji wyników.
Zmień właściwość ReadOnly komponentu Memo2 na True, żeby nie można było zmieniać treści prezentowanej w tej kontrolce.
Możesz wpisać przykładowy blok PL/SQL w komponencie Memo1. W tym celu kliknij dwukrotnie właściwość Lines i w oknie String List Editor wpisz treść bloku PL/SQL. Ja wpisalem:

declare
   C NUMBER;
begin
   SELECT Count(*)
   INTO :C
   FROM artur.ksiazki;
end;


Umieść na formatce komponent Button1 i zmień jego właściwość Caption na Wykonaj blok PL/SQL.
Oprogramuj zdarzenia onShow i onDestroy formularza. W zdarzeniu onShow będzie następowało połączenie z bazą danych, w zdarzeniu onDestroy rozłączanie z bazą danych

procedure TForm1.FormShow(Sender: TObject);
begin
    OraSession1.Connect; //połącz
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
    OraSession1.Disconnect; //rozłącz
end;


Oprogramuj zdarzenie onClick przycisku Button1. Naciśnięcie przycisku będzie powodowało wykonanie bloku PL/SQL.

procedure TForm1.Button1Click(Sender: TObject);
var
   i:integer;
begin
   //wczytaj zawartość pola Memo do właściwości SQL komponentu OraSQL1
   OraSQL1.SQL :=Memo1.Lines;
   //sprawdź, czy wśród parametrów jest parametr, który nie ma określonego typu
   for i := 0 to OraSQL1.Params.Count-1 do
      //jeśli tak jest to ...
      if OraSQL1.Params[i].DataType = ftUnknown then
         //... przypisz typ string parametrowi
         OraSQL1.Params[i].DataType := ftString;
   OraSQL1.Execute; //wykonaj blok PL/SQL
end;


W pętli wyszukiwane są parametry bloku Pl/SQL. Jeśli dowolny z parametrów nie ma zadeklarowanego typu, to przypisujemy mu typ łańcuchowy, ponieważ każdy parametr musi mieć zdefiniowany typ.

Oprogramuj zdarzenie AfterExecution komponentu OraSQL1. Zdarzenie to zachodzi po wykonaniu bloku PL/SQL. My w tym zdarzeniu wypiszemy nazwy i wartości parametrów.

procedure TForm1.OraSQL1AfterExecute(Sender: TObject; Result: Boolean);
var
  i:integer;
begin
  if Result then  //jeśli coś zostało zwrócone
  begin
    Memo2.Lines.Clear; //wyczyść komponent Memo2
    //w pętli wypisz nazwy wszystkich parametrów i ich wartości
    for i := 0 to OraSQL1.Params.Count-1 do 
       Memo2.Lines.Add(OraSQL1.Params[i].Name + ' = ' + OraSQL1.Params[i].AsString);
  end
  else //w przeciwnym wypadku wyświetl komunikat
     showmessage('Nic nie zostało zwrócone lub wystąpił błąd');
end;


</ol>

Nazwę parametru można uzyskać poprzez wywołanie instrukcji
OraSQL1.Params[i].Name 

natomiast jego wartość można uzyskać np. poprzez instrukcję
OraSQL1.Params[i].AsString.




Cały listing programu wygląda nastepująco:

unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DBAccess, Ora, DB;
 
type
  TForm1 = class(TForm)
    OraSession1: TOraSession;
    OraSQL1: TOraSQL;
    Memo1: TMemo;
    Button1: TButton;
    Memo2: TMemo;
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure OraSQL1AfterExecute(Sender: TObject; Result: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.FormShow(Sender: TObject);
begin
    OraSession1.Connect;
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
    OraSession1.Disconnect;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
   i:integer;
begin
   OraSQL1.SQL :=Memo1.Lines;
   for i := 0 to OraSQL1.Params.Count-1 do
      if OraSQL1.Params[i].DataType = ftUnknown then
         OraSQL1.Params[i].DataType := ftString;
   OraSQL1.Execute;
end;
 
procedure TForm1.OraSQL1AfterExecute(Sender: TObject; Result: Boolean);
var
  i:integer;
begin
  if Result then
  begin
    Memo2.Lines.Clear;
    for i := 0 to OraSQL1.Params.Count-1 do
       Memo2.Lines.Add(OraSQL1.Params[i].Name + ' = ' + OraSQL1.Params[i].AsString);
  end
  else
     showmessage('Nic nie zostało zwrócone lub wystąpił błąd');
end;
 
end.



Podsumowanie ODAC



W części tej zostala zaprezentowana nowoczesna technologia dostępu do baz danych oracle 10g - ODAC, która nie jest standardowo dostępna w Delphi. Dużo więcej informacji znajdziecie w książce - m.in znajdziecie tam obsługę atrybutów typu BLOB za pomocą ODAC i tworzenie aplikacji internetowych za pomocą ODAC i IntraWeb.

Tworzenie niestandardowej siatki



DBGrid jest złożoną kontrolką. Posiada wiele opcji i możliwości. Wiele z tych opcji jest jednak nieznanych. W książce znajdziecie sposoby:

kolorowania siatki,
tworzenia siatki z polem wyboru,
tworzenia siatki pozwalajacej wyświetlać grafikę,
tworzenia siatki pozwalajacej na zaznaczanie wielu rekordów,
tworzenia siatki z polem Memo
</ul>

Teraz zaprezentuję tylko sposób dopasowywania szerokości kolumn do zawartości siatki.

Dopasowanie szerokości kolumn



Bardzo często deklarując atrybut typu łańcuchowego w Oracle rezerwujemy wielkość pamięci dużo większą niż ten atrybut będzie faktycznie przechowywał. Np. deklarując atrybut Nazwisko z tabeli Autorzy zarezerwowaliśmy dla niego 30 znaków, choć bardzo rzadko można spotkać nazwiska dłuższe niż 20-literowe. Jeśli teraz napiszesz aplikację w Delphi i wyświetlisz dane w DBGrid, to okaże się, że kolumna tabeli przechowująca określony atrybut jest po prostu za szeroka i że trzeba przewijać siatkę, żeby zobaczyć pozostałe kolumny. Można temu zaradzić tworząc odpowiednią procedurę, która najpierw obliczy w pikselach długość najdłuższego wpisu w danej kolumnie, a następnie dopasuje jej szerokość do obliczonej wartości.

procedure Ustaw_dlugosc_siatki(DBGrid: TDBgrid);
const
  dodatkowa_dlugosc = 10;
var
  pomoc, n: Integer;
  max_dlugosc: array [0..30] of Integer;
begin
  with DBGrid do
  begin
    Canvas.Font := Font;
    //zapamiętanie długości napisów w tytułach kolumn
    for n := 0 to Columns.Count - 1 do
      max_dlugosc[n] := Canvas.TextWidth(Fields[n].FieldName) +  dodatkowa_dlugosc;
    DataSource.DataSet.First;
    //jeśli pole jest dłuższe niż długość kolumny, to ustawiamy nowy max_dlugosc
    while not DataSource.DataSet.EOF do
    begin
      for n := 0 to Columns.Count - 1 do
      begin
        pomoc := Canvas.TextWidth(trim(Columns[n].Field.DisplayText)) + 
                                                  dodatkowa_dlugosc;
        if pomoc > max_dlugosc[n] then max_dlugosc[n] := pomoc;
      end;
      DataSource.DataSet.Next;
    end;
    //ustawiamy nową szerokość kolumn
    DataSource.DataSet.First;
    for n := 0 to Columns.Count - 1 do
      if max_dlugosc[n] > 0 then
        Columns[n].Width := max_dlugosc[n];
  end;
end;


W celu zapamiętania szerokości (w pikselach) kolumn zadeklarowaliśmy trzydziestoelementową tablicę max_dlugosc. Dlaczego trzydziestoelementową? Ponieważ rzadko zdarza się, by tabela miała więcej atrybutów. Oczywiście można posłużyć się tablicami dynamicznymi, aby liczba elementów odpowiadała faktycznej liczbie kolumn w siatce, ale nie to jest istotą prezentowanego rozwiązania. Najpierw do tablicy wpisujemy długość tytułów kolumn. Następnie dokonujemy sprawdzenia, czy którekolwiek pole w tej kolumnie ma większą długość niż tytuł kolumny. Jeśli tak, to ustawiamy nową wartość w tablicy max_dlugosc. Na koniec trzeba ustawić szerokość kolumn. Jak pewnie zauważyłeś, do wartości określającej długość napisu dodawana jest wartość przechowywana przez stałą dodatkowa_dlugosc. Należy to zrobić po to, żeby komórka tabeli była odrobinę większa niż długość przechowywanego przez nią napisu.
Aby teraz dopasować szerokość DBGrid należy w zdarzeniu (np. onShow formularza) wywołać tę procedurę

begin
    Ustaw_dlugosc_siatki(DBGrid1);
end;


Rysunek


Raportowanie bazy danych Oracle



Rave Reports - raportowanie za pomocą kodu



Teraz pokażemy, w jaki sposób umieszczać w raporcie dane pobrane z bazy danych stworzonej w Oracle 10g. Dane do raportu pobierzemy wykorzystując technologię ADO, lecz bardzo podobnie sporządza się raporty przy użyciu danych pobranych za pomocą innych technologii. Do stworzenia raportów posłużymy się Rave Reports (więcej o raportowaniu w Rave Reports możesz znaleźć w dwóch moich artykułach umieszczonych na 4programmers i oczywiście w książce).

Stwórzmy raport pokazujący ISBN, tytuł, podtytuł i liczbę stron wszystkich pozycji z tabeli Ksiazki.


Umieść na formularzu komponent ADOConnection1 i skonfiguruj łańcuch połączeń.
Umieść na formularzu komponenty ADOQuery1, RvSystem1 i Button1. Zmień właściwość Caption komponentu Button1 na Pokaż dane o książkach.
Ustaw właściwość Connection komponentu ADOQuery1 na ADOConnection1
W sekcji private formularza umieść deklarację procedury:

procedure Raportoksiazkach(Report : TBaseReport);


Oprogramowujemy zdarzenia onPrint komponentu RvSystem1 i onClick komponentu Button1.

procedure TForm1.Button1Click(Sender: TObject);
begin
    RvSystem1.Execute;
end;
 
procedure TForm1.RvSystem1Print(Sender: TObject);
begin
    Raportoksiazkach(Sender as TBaseReport);
end;


Pod słowem implementation napisz kod procedury Raportoksiazkach

procedure TForm1.Raportoksiazkach(Report: TBaseReport);
var
    i:integer;
begin
    //tworzymy tytuł raportu
    Report.SetFont('Arial', 15);
    Report.NewLine; 
    Report.PrintCenter('Książki', 4);
    Report.NewLine;  
    Report.NewLine; 
    Report.ClearTabs; 
    //ustawiamy właściwości kolumn, jednostkami są cale
    Report.SetTab(0.2, pjLeft, 1.7, 0, 0, 0
    Report.SetTab(1.9, pjLeft, 2.1, 0, 0, 0);
    Report.SetTab(4.0, pjLeft, 2.5, 0, 0, 0);
    Report.SetTab(6.5, pjRight, 1.5, 0, 0, 0);
    Report.SetFont('Arial', 10); 
    Report.Bold := True;
    //tworzymy odpowiednie nagłówki
    Report.PrintTab('ISBN'); //nagłówek pierwszej kolumny
    Report.PrintTab('Tytul książki'); //nagłówek drugiej kolumn    
    Report.PrintTab('Podtytul'); //nagłówek trzeciej kolumny
    Report.PrintTab('Liczba stron'); //nagłówek czwartej kolumny
    //tworzymy komórki tabeli i uzupełniamy je danymi pobranymi z tabeli Ksiazki
    Report.Bold := False; 
    Report.NewLine; 
{zamykamy komponent ADOQuery1, czyścimy jego zawartość SQL, wstawiamy nową treść zapytania i wykonujemy wpisane zapytanie ? standardowa operacja znana z rozdziału 8.}
    ADOQuery1.Close;
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('SELECT ISBN, Tytul, Podtytul, Liczba_stron FROM Ksiazki');
    ADOQuery1.Open;
    ADOQuery1.First; //ustawiamy wskaźnik na pierwszy wpis
    for i:=1 to ADOQuery1.RecordCount do //dopóki są rekordy w zbiorze danych
    begin
        //wyświetlij odpowiednie pole
        Report.PrintTab(ADOQuery1.FieldByName('ISBN').AsString);
        Report.PrintTab(ADOQuery1.FieldByName('Tytul').AsString);
        Report.PrintTab(ADOQuery1.FieldByName('Podtytul').AsString);
        Report.PrintTab(ADOQuery1.FieldByName('Liczba_stron').AsString);
        Report.NewLine; //przejdź do nowej linii
        ADOQuery1.Next; //przejdź do następnego rekordu w zbiorze danych
    end;
end;

</ol>

Tworzenie raportu kolumnowego z danymi pobranymi z bazy danych nie różni się specjalnie od tworzenia zwykłego raportu kolumnowego. Najpierw tworzony jest nagłówek raportu i nagłówki kolumn tabeli a astępnie za pomocą instrukcji:

ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add('SELECT ISBN, Tytul, Podtytul, Liczba_stron FROM Ksiazki');
ADOQuery1.Open;


wykonywane jest zapytanie SQL i dane przechowywane są przez komponent ADOQuery1 w postaci zbioru danych. Aby wyświetlić wszystkie dane, trzeba ustawić wskaźnik na początek zbioru danych za pomocą instrukcji:

ADOQuery1.First;


Następnie w pętli dane są wypisywane. Do konkretnego pola ze zbioru danych można dostać się za pomocą:

ADOQuery1.FieldByName('Atrybut_tabeli').AsString


Po przejściu do następnej linii w raporcie za pomocą instrukcji

Report.NewLine;


należy przejść do następnego wpisu w zbiorze danych, czyli wykonać instrukcję:

ADOQuery1.Next


Raporty tworzone z danych pobieranych z bazy danych generowane są dłużej niż zwykłe raporty Rave Reports. Związane jest to z czynnościami pobierania danych z bazy Oracle 10g. Dlatego przed ukazaniem się raportu użytkownik będzie mógł zobaczyć okno Report Status informujace o tym ktora strona raportu jest generowana. Po wygenerowaniu wszystkich strona na ekranie zostanie wyświetlony raport.

Rysunek

Cały kod programu tworzącego raport kolumnowy z danych pobranych z bazy został przedstawiony poniżej.

unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, RpDefine, RpBase, RpSystem, DB, ADODB;
 
type
  TForm1 = class(TForm)
    ADOQuery1: TADOQuery;
    RvSystem1: TRvSystem;
    Button1: TButton;
    ADOConnection1: TADOConnection;
    procedure RvSystem1Print(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure Raportoksiazkach(Report : TBaseReport);
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.Raportoksiazkach(Report: TBaseReport);
var
    i:integer;
begin
    Report.SetFont('Arial', 15);
    Report.NewLine;             
    Report.PrintCenter('Książki', 4);
    Report.NewLine; 
    Report.NewLine; 
    Report.ClearTabs; 
    Report.SetTab(0.2, pjLeft, 1.7, 0, 0, 0);
    Report.SetTab(1.9, pjLeft, 2.1, 0, 0, 0);
    Report.SetTab(4.0, pjLeft, 2.5, 0, 0, 0);
    Report.SetTab(6.5, pjRight, 1.5, 0, 0, 0);
    Report.SetFont('Arial', 10);
    Report.Bold := True; 
    Report.PrintTab('ISBN'); 
    Report.PrintTab('Tytuł książki'); 
    Report.PrintTab('Podtytuł'); 
    Report.PrintTab('Liczba stron');
    Report.Bold := False; 
    Report.NewLine; 
    ADOQuery1.Close;
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('SELECT ISBN, Tytul, Podtytul, Liczba_stron FROM Ksiazki');
    ADOQuery1.Open;
    ADOQuery1.First;
    for i:=1 to ADOQuery1.RecordCount do
    begin
         Report.PrintTab(ADOQuery1.FieldByName('ISBN').AsString);
         Report.PrintTab(ADOQuery1.FieldByName('Tytul').AsString);
         Report.PrintTab(ADOQuery1.FieldByName('Podtytul').AsString);
         Report.PrintTab(ADOQuery1.FieldByName('Liczba_stron').AsString);
         Report.NewLine;
         ADOQuery1.Next;
    end;
end;
 
procedure TForm1.RvSystem1Print(Sender: TObject);
begin
    Raportoksiazkach(Sender as TBaseReport);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
    RvSystem1.Execute;
end;
 
end.



Podsumowanie



Wydaję mi się, że w artykule tym przedstawiłem dużo ciekawych aspektów programowania baz danych Oracle 10g w środowisku Delphi. Wybaczcie, jeśli artykuł nie jest napisany spójnie, ale pamiętajcie, że wybrałem tylko niektore zagadnienia związane z oracle 10g i Delphi (zabrakło np. informacji o transakcjach, a także o tysiącu innych rzeczy). Ale myślę, że i tak artykuł jest dość obszernym źródłem wiedzy. Jest on również reklamą książki "Oracle 10g i Delphi. Programowanie baz danych", której jestem współautorem. Więc jeśli kogoś z Was przekonałem w ten sposób, że warto kupić tę książkę, to jest mi miło z tego powodu i odsyłam do strony http://helion.pl/ksiazki/oradel.htm

Przepraszam natomiast wszystkich tych, którym przeszkadza to, że "wieje" od tego artykułu reklamą - nie czytajcie tych zdań (stwierdzeń), które zaczynają się od słów "więcej informacji znajdziecie..." :)

10 komentarzy

gilbert3 2008-06-03 13:28

AnyDac - świetne i darmowe komponenty do współpracy z Oracle i innymi popularnymi bazami danych.

emmamarta 2007-06-23 14:00

Witam, Bardzo dobra książka, niestety kupiłam ja z myslą ze znajde odpowiedz na moje pytanie i niestety nie znalazłam. Ale dla innych wiadomości warto było kupić. Moze znajdzidzie sie ktos kto zechce mi pomóc. Mam wgrane do bazy tabelkę z nazwą zdjęcia i zdjęciem (StillImage). W sql/plusie wszystko ładnie wyszukuje, np znajdz zdjęcia podobne do 12.jpg ze względu na kolor i jest ok. jednak nie wiem jak podpiąc te zdjęcia na stronie np za pomoca Oracle HTTP Server zeby te zdjęcia pokazywało, a nie tylko sane nazwy zdjęć. Bardzo prosze o pomoc. Prosze o kontakt na maila [email protected] Moze chociaz jakies materiały, artykuły na ten temat. Z góry dziekuje.

RedbaK 2006-07-10 10:41

Bardzo dobry artykuł. Zresztą jak każdy Artura.
Do zbyszekz10:
Idź się ponapinaj na podwórko ze swoimi waflami. Widać, że z niego zawistny polaczek.
Ktoś wykonał porządną pracę, a drugi zaraz musi po tym jechać.

Artur 2006-02-25 19:51

Parę słów do zbyszekz10: Gościu chciało ci się specjalnie rejestrować na 4p, żeby napisać głupi komentarz (nie masz nawet żadnego posta na tym forum - więc zarejestrowałeś się po to, żeby tylko dokopać)? Sprawia ci przyjemność jak komuś dokopiesz, nienawidzisz ludzi, masz kompleksy, mama cię biła jak byłeś mały, czy znasz mnie i strasznie cię kole w oczy fakt wydania książki? Bo nie wiem czym tłumaczyć twoją wypowiedź. Jakbyś się znał na temacie to byś napisal jakie są nieścislości. Może ty napisz jakiegoś posta, odpowiedź w FAQ, artykuł i zobaczymy wtedy jak profesjonalny będzie to tekst.

Nie oczekuję zachwytów nad tekstem, ale sensownych komentarzy, celnych uwag, lub napisania, gdzie jest jakaś nieścisłość.

Mam kilka uwag do twórców systemu. Strasznie nie podoba mi się, że mój tekst może każdy edytować - wejdzie np. taki zbyszekz10 i powstawia mi jakieś zdjęcia gołych panienek, dopisze jakieś głupoty, a ja będę podpisany pod tym tekstem. Druga sprawa to tekst "ostatni autor" w Informacjach pod artykulem - nie powinno być czegoś takiego, że jak ktoś poprawi mi literówkę to jest autorem tekstu, w który ja wlożyłem sporo pracy. Powinien być wyświetlany tekst kto jest autorem.

Adam Boduch 2006-02-24 14:07

Ewentualne niescislosci zawsze mozna poprawic zbyszekz10... kazdy moze klikajac w przycisk "Edycja".... bo ksiazki to sie juz raczej nie poprawi ;)

zbyszekz10 2006-02-21 10:07

Reklama czy antyreklama?
Jeśli w tym tekście jest kilka nieścisłości to jak wygląda cała książka?

Adam Boduch 2006-02-19 17:33

Polecam zapoznac sie z tekstem "Formatowanie tekstow" w dziale Pomoc :) Dostepny jest specjalny znacznik, ktory sam generuje spis tresci. Poprawilem nieco tekst, lecz nadal niektore elementy nie sa zgodne z XHTML.

brodny 2006-02-18 22:40

<quote>Przepraszam natomiast wszystkich tych, którym przeszkadza to, że "wieje" od tego artykułu reklamą</quote>
Jeśli mogę wtrącić swoje 3 grosze, to powiem, że w tym wypadku mnie to nie przeszkadza. Odwaliłeś kawał dobrej roboty pisząc książkę, więc reklama się należy. A że przy okazji jako autor podzieliłeś się z nami częścią swojej pracy - chwała Ci za to. Byle więcej takich form reklamy - nikt nie kupiłby wtedy przysłowiowego kota w worku, wiemy, za co płacimy :)

adikrup 2006-03-11 20:09

Od tygodnia jestem szczęśliwym posiadaczem tej książki  .korzystając ze wskazówek zawartych w książce pisze swoja pierwszą aplikacje w BCB + ORACLE .
korzystajac z okazji mam pytanie
z EM i ISQL*PLUS lacze sie z baza bez problemow.natomiast przy probie podlaczenia z aplikacji w technologi dbexpress pojawia sie komunikat ORA12154:TNS:nie mozna wyliczyc nazwy uslugi.

prosze o pomoc