1.1Delphi數據庫應用程序的層次結構
Delphi數據庫應用程序通過數據存取構件對數據庫進行訪問,通過可視的數據構件(Data Control)將數據呈現給用戶,並與用戶進行交互。Delphi數據庫應用程序的結構如下圖:
圖 2‑1
Delphi訪問數據庫可以分爲三級:
(1)、由ADO(Active Data Object)對象及底層數據庫驅動程序來訪問數據源。
(2)、再由數據集構件(TADOTable、TADOQuery、TADODataSet)與ADO對象進行通信。
(3)、構件TDataSource作爲數據集構件與數據感知構件(TDBEdit、TDBText、TDBCheck、…)之間的橋樑,負責將來自數據集構件的數據傳送給數據感知構件。
圖 2‑2 數據感知組件、DataSource、數據集對象及數據庫四者之間關係及屬性設置
1.2什麼是數據集
1.2.1什麼是數據集?
數據集,在Delphi中就是具有TDataSet類型的對象。
數據集是把數據庫中的數據映射到內存緩存中的所構成的數據容器。從數據庫抽取數據後,DataSet就是數據的存放地,所以有時說DataSet可以看成是一個數據容器,同時它在客戶端實現讀取、更新數據庫等過程中起到了中間部件的作用。如果省略上圖中的DataSource組件,就得到下圖:
圖 2‑3 什麼是數據集
在Delphi中,應用程序都是通過各類數據集構件(如:TADOTable、TADOQuery、TADODataSet等)來存取數據庫中的數據,所以,掌握數據集構件的使用,對開發數據庫應用程序至關重要。
1.2.2數據集構件的繼承關係
所有數據集構件都是由TDataSet繼承而來,但由於TDataSet是一個抽象類,所以不能直接由此創建對象並使用它(如同TStrings類),而必須使用其派生類,如:TADOTable、TADOQuery、TADODataSet和TADOStoredProc 等。這些派生類統稱爲“數據集構件”,位於ADO 組件面板上。
這些數據集構件都是從一個共同的父類TDataSet繼承下來的,其繼承關係可以用圖3.1來表示。
圖 2‑4 數據集構件的繼承關係
正因爲TADOTable、TADOQuery、TADODataSet和TADOStoredProc都是從TDataSet繼承而來的,因而這四個構件具有許多相同的屬性、方法、事件,這些屬性、方法、事件我們會在隨後的各章節中結合例子逐漸介紹。
在後面各章節中,我們會大量用到TADOTable、TADOQuery、TADODataSet和TADOStoredProc這四類數據集構件。
1.3數據集的記錄位置
1.3.1改變記錄位置
在第一個例子中我們看到,當單擊DBNavigator構件上的“首記錄”、“前一條記錄”、“後一條記錄”、“尾記錄”按鈕時,DBGrid構件左邊有一個三角形標記“„”,該標記用於指示當前記錄位置。
我們在存取或刪除記錄時,都是以一條記錄爲單位,即:我們只能存取或刪除當前那條記錄。
當我們打開一張表時,當前記錄位置總是在第一條記錄上。如果要改變記錄位置,可以使用以下幾種方法:
(1)、鼠標單擊DBGrid構件中的某一行(或用光標鍵將當前光標移到某一行),則該行記錄就成爲當前記錄。
(2)、DBNavigator構件上的四個按鈕。
(3)、使用數據集構件的五個方法:
方 |
說 |
First |
將當前記錄位置移至首記錄 |
Prior |
將當前記錄位置移至上一條記錄 |
Next |
將當前記錄位置移至下一條記錄 |
Last |
將當前記錄位置移至尾記錄 |
MoveBy |
將當前記錄位置向前/後移動若干個位置 |
例 2‑1使用上述數據集構件ADOTable的5個方法程序來移動記錄。
圖 2‑5
4個命令按鈕的OnClick事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject); //首記錄
begin
ADOTable1.First;
end;
procedure TForm1.Button2Click(Sender: TObject); //上一條
begin
ADOTable1.Prior;
end;
procedure TForm1.Button3Click(Sender: TObject); //下一條
begin
ADOTable1.Next;
end;
procedure TForm1.Button4Click(Sender: TObject); //尾記錄
begin
ADOTable1.Last;
end;
1.3.2數據集構件的BOF與EOF屬性
這兩個屬性具有邏輯值,即其值爲True或False。當出現下列幾種情況時,BOF(EOF)屬性值變爲True:
操 作 |
結 果 |
執行First方法 |
BOF值變爲True |
執行Prior方法並超出首記錄位置時 |
BOF值變爲True |
執行Last方法 |
EOF值變爲True |
執行Next方法並超出尾記錄位置時 |
EOF值變爲True |
打開空表時(沒有任何記錄) |
BOF、EOF值均變爲True |
其餘情況下,BOF與EOF屬性值總是False。
我們需要遍歷表中所有記錄時,可以利用這兩個屬性。
例 2‑2統計出Students.mdb數據庫的Students表中的姓“廖”的記錄數。
圖 2‑6
“統計”命令按鈕的OnClick事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject);
var s:integer;
begin
end;
另外,數據集構件有一個RecordCount屬性,它返回數據集中記錄的總數。使用該屬性,上述程序也可以改寫爲:
for i:=1 to adotable1.RecordCount do
begin
……
end;
1.4數據集的字段對象
數據集構件有一個Fields屬性,該屬性是一個下標從0開始的數組,數組的每個元素代表數據集構件中的各個字段。例如:Fields[0]代表第一個字段、Fields[1] 代表第二個字段、… 。而每個Fields[i]又是一個TField類型的對象(稱爲字段對象)。熟悉並掌握字段對象的使用,對於熟練使用Delphi開發數據庫應用程序具有十分重要的意義。
1.4.1 字段對象(TField)的屬性
這是一個非可視的對象,因而並不出現在Delphi的組件面板上。它的許多屬性描述了數據集中每一列的數據類型、當前值、編輯格式等。
缺省情況下,每次在數據集構件激活時(即:打開數據集),Delphi自動爲數據集中的每個字段分別創建一個TField對象,關閉數據集時,這些字段對象便釋放掉,因而稱爲臨時性字段對象。
TField對象的幾個常用屬性
屬 |
說 |
FieldName |
字段名 |
Index |
字段在數據集中的序數值 |
Value |
字段值(根據不同的字段類型,字段值的數據類型也不同) |
Text |
數據感知構件處於編輯狀態時所顯示的文本(字符串類型) |
IsNull |
字段值是否爲空值 |
例 2‑3列出Students.mdb數據庫的Students表中所有的字段。
圖 2‑7
這個例子中,由於不需要使用數據感知構件,所以就沒有必要使用DataSource構件,只要一個數據集構件ADOTable就行了。
創建應用程序界面之後,添加命令按鈕Button1的OnClick事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
for i:=0 to adotable1.FieldCount-1 do
end;
|
利用字段對象Fields[i]的Value屬性可以獲取字段值。如想要取得表Students中第i個字段的值並顯示在編輯框Edit1中,可以執行:
Edit1.text:=ADOTable1.Fields[i].Value;
但更好的方法是使用如下的幾個屬性:TField對象的數據轉化屬性
屬 |
說 |
AsBoolean |
將當前字段值轉換爲布爾類型 |
AsDateTime |
將當前字段值轉換爲日期類型 |
AsFloat |
將當前字段值轉換爲浮點類型 |
AsInteger |
將當前字段值轉換爲整數類型 |
AsString |
將當前字段值轉換爲字符串類型 |
這些屬性同樣表示字段值,且自動會轉換成你所需要的數據類型。
所以,上述代碼可改爲:Edit1.text:=ADOTable1.Fields[1].AsString;
1.4.2 字段對象(TField )的幾種表示形式
字段對象表示形式可以有如下幾種表示形式:
(1)、ADOTable1.Fields[i]
這裏,Fields[i]是數據集構件ADOTable的屬性,並且是數組屬性,下標從0開始。
(2)、ADOTable1.FieldByName(<字段名>)
這裏,FieldByName是數據集構件的一個方法,該方法返回值即爲由<字段名>指定的那個字段對象。
(3)、ADOTable1.FindField(<字段名>)
FindField也是一個方法,與FieldByName基本相同,唯一不同之處是,FindField中如果指定的字段不存在,則返回Nil(空指針)。
當知道字段名時,用(2)比較合適;反之當不知道字段名,而只知道字段的順序號時,則只能使用(1)。再有,當要求列出一張表中的全部字段時,也只能使用(1)的辦法,通過一個循環來實現(見上一個例子)。
1.4.3獲取字段值的幾種方法
通過字段對象獲取字段值的幾種辦法:
(1)、使用<字段對象>.Value或<字段對象>.AsString、<字段對象>.AsInteger、….等
如前面所提到的ADOTable1.Fields[1].AsString
或ADOTable1.FieldByName(‘姓名’).AsString
(2)使用數據集構件的FieldValues屬性,該屬性也是一個數組屬性,下標從0開始。
如:ADOTable1.FieldValues[‘姓名’]
就表示當前記錄的姓名字段的字段值。
由於FieldValues是數據集構件的默認屬性,所以可省略FieldValues,而直接寫成
ADOTable1 [‘姓名’]
這種寫法較爲簡潔,推薦讀者使用。
TFields部件對應於數據庫表中的實際字段,它既可以在應用程序的運行過程中動態地生成也可以在程序設計階段用字段編輯器創建。它是不可見的部件,在程序中我們可以通過TField部件來訪問數據庫記錄的各個字段值。
1.4.4 永久字段對象
1、動態字段對象
默認情況下,當把一個數據集構件(如:ADOTable)放到窗體上並且打開它時,Delphi 就會爲數據集中的每一個字段自動生成一個動態的字段對象。之所以說它是動態的,一方面是因爲它是自動生成的,另一方面是因爲它總是反映物理數據集的情況,對於不同類型的字段來說,生成的字段對象的類型也不同。如果數據集的結構或其他信息發生變化,當應用程序重新打開這個數據集時,就會基於最新的結構和信息重建所有的字段對象。
前面所講到的字段對象的幾種表示方法中,都是動態字段對象。動態字段對象的生存期是臨時的,當數據集關閉時,這些字段對象也跟着消失。
動態字段對象的不足之處是,要改變字段的顯示屬性、數據格式,需編寫代碼。即使你願意寫代碼,無論如何也無法改變字段的顯示順序,不能把某些字段暫時隱去,也不能增加新的字段如“計算字段”、“Lookup字段”。而且,也不能改變字段的數據類型。所以,很多情況下,應用程序往往要用永久字段對象來代替動態字段對象。
2、
用永久字段對象代替動態字段對象的最大好處是,可以在設計期設置它的屬性。此外,永久字段對象還具有以下優勢:
u
u
u
3、
要創建永久字段對象,就要用到字段編輯器。創建永久字段對象的一般步驟是:
雙擊窗體中的數據集構件ADOTable,就會打開字段編輯器,這時,字段編輯器窗口中是空的,也就是說我們還沒有創建任何永久性的字段對象。
圖 2‑8
再在字段編輯器窗口中單擊右鍵,彈出快捷菜單,選擇其中的
圖 2‑9
“Add Fields…”:選擇這個命令會出現“Add Fields”窗口,窗口中顯示的是當前數據集構件所連接的數據庫表中的所有字段,我們可以選取需要的字段,將它們加入到字段編輯器窗口中。
圖 2‑10
“New Fields…”:選擇這個命令,將會出現“New Field”對話框,在對話框中可以新增“計算字段”和“Lookup字段”(後面章節將作介紹)。
圖 2‑11
“Add all Fields…”:當前數據集構件所連接的數據庫表中的所有字段都加入到字段編輯器窗口中。
已經添加到字段編輯器窗口中永久字段對象,如果覺得不需要,可以刪除,方法是:重新打開字段編輯器窗口,選中所要刪除的字段對象,按<Delete> 鍵即可。
4、
雙擊數據集構件ADOTable,打開字段編輯器窗口,選中其中的永久性字段對象,將其拖放到窗體上,Delphi會首先自動創建一個DataSource構件,然後自動給每個字段創建一個合適的數據感知構件,並自動設置每個數據感知構件的DataSource屬性與。
圖 2‑12
圖 2‑13
這樣,對於設計一些數據錄入窗體,可以大大減少程序設計者的工作量。
5、
我們知道,Delphi中,所有變量都具有某種類型(Integer、String、Boolean等);同樣,所有對象也都具有各自的類型(對象的類型有個特別的名稱,稱爲“類”-Class)。
動態字段對象都只有一個籠統的類型:TField,沒有具體的類型。而永久性字段對象則具有具體的數據類型。如:當使用字段編輯器創建如圖 2‑14所示的永久性字段對象後,選擇該字段對象,就會在Object Inspector窗口中看到該字段對象的名稱及所屬的數據類型。
圖 2‑14
從圖 2‑14可以看到jbgz字段所對應的字段對象名稱爲:ADOTable1jbgz,該名稱是Delphi自動給出的,而這個字段對象的數據類型爲:TIntegerField,這個類型其實是從TField類型繼承下來的(不是直接繼承)。見圖 2‑15所示。
圖 2‑15
前面講到,要引用字段對象,可以使用ADOTable1.Fields[i]或ADOTable1.FieldByName( ),現在,當我們創建了永久性字段對象後,還可以直接使用永久字段對象名來引用該字段對象。如:要取得圖 2‑15中jbgz字段的值,除了可以使用
ADOTable1.Fields[5].AsString 或
ADOTable1.ByName(‘jbgz’).AsString
還可以直接使用ADOTable1jbgz.AsString 。
另外,搞清楚上述概念後,在代碼編輯窗口書寫程序,當輸入:
ADOTable1.FieldByName(‘jbgz’).
之後,Delphi自動彈出的屬性、事件、方法列表與輸入:
ADOTable1jbgz.
之後彈出的屬性、事件、方法列表可能不完全相同,這是因爲ADOTable1jbgz對象是從TField繼承下來的,比TField類型的對象具有更多的屬性、事件、方法可供使用。
|
1.5數據集中記錄的增加、刪除、編輯
1.5.1添加記錄
步驟:
(1)、調用數據集構件的Append方法或Insert方法,添加一條空白記錄,然後用手工方式或通過執行如下代碼來填入內容:
ADOTable1[‘字段名’]:=<表達式>
(2)、調用數據集構件的Post方法,將緩衝區中的數據真正寫入表中:
ADOTable1.Post
另外,通過移動記錄位置也可以將緩衝區中的數據寫入表中。例如:在DBGrid構件中直接用手工方式輸入記錄時,每輸完一條記錄後,直接將光標移到下一行,則上一行記錄便自動保存,而無需執行Post方法。
注:在執行Post方法之前,如果想撤消所填入的內容,則可執行Cancel 方法來達此目的:
ADOTable1.Cancel
1.5.2 編輯記錄
對於少量一更改,可以在DBGrid中通過手工方式來逐條更改記錄,但如果是批量更改,則需要用程序來實現,而手工方式的效率太低了。
通過程序實現記錄更改,其步驟爲:
(1)、調用數據集構件的First、Prior、Next、Last方法,將記錄指針移到所要更改的那條記錄上。
(2)、調用Edit方法,使數據集構件處於編輯狀態。
(3)、通過如下賦值語句:
ADOTable1[‘字段名’]:=<表達式>
給指定的字段賦值。
(4)、執行Post方法,將緩衝區中的數據真正寫入表中。
例2‑4將Employee.mdb數據庫的Employee表中凡所屬縣市爲“臺北市”的職工“目前月薪資”增加100元。
圖 2‑16
命令按鈕Button1的OnClick的事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
'目前月薪資').asinteger:=adotable1.FieldByName('目前月薪資').asinteger+100;
end;
在後面學了SQL-UPDATE命令後,上述程序就可以用一條命令來代替。
1.5.3 刪除記錄
要刪除一條記錄,只要執行數據集構件的Delete方法。該方法可以刪除當前記錄指針所指的那條記錄。
當然,在刪除記錄之前,最好先提示用戶:是否真的要刪除該條記錄?
如:
if application.MessageBox('是否真的要刪除當前記錄?',
'警告',MB_OKCANCEL+MB_ICONQUESTION)=IDOK then
|
1.6 查找數據
1.6.1 Locate方法
數據集構件有一個用於按條件快速定位記錄位置的方法:Locate。該方法聲明如下:
function Locate(const KeyFields: String; const KeyValues: Variant; Options: TLocateOptions): Boolean;
該方法是一個函數,具有返回值:當查找到滿足條件的記錄時,返回True;否則返回False。
函數的各個參數說明如下:
KeyFields:所要查找的字段名
KeyValues:所要查找的字段值
Options:這是一個數據類型爲TLocateOptions的參數
TlocateOptions的聲明如下:
TLocateOptions= set of
從該聲明可以看出,TlocateOptions是一個集合類型,該集合的基類型爲枚舉類型。所以,TlocateOptions類型的變量可以取的值爲:[ ]、[loCaseInsensitive]、[loPartialKey]、[loCaseInsensitive, loPartialKey] ,這四取值分別表示:
u
u
u
u
例2‑5在Students.mdb數據庫的Students表中根據學生姓名查找記錄。
圖 2‑17
注意:本例無需使用數據感知構件,查找結果只需用Label構件顯示即可。
//“查找”命令按鈕的OnClick事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
end;
1.6.2記錄過濾
使用Locate方法,只能按照是否與某個字段值相匹配的方式來查找記錄,如果遇到要查找“語文成績>60分”這樣的情形時,如何實現呢?答案是:使用記錄過濾。
數據集構件有一個Filter屬性,該屬性爲字符串類型,只要將查找條件(邏輯表達式)以字符串的形式賦值給該屬性,同時再將另一個屬性Filtered設置爲True就可以了。
如:對於上述查找“語文成績>60分”的記錄,只要執行如下語句:
ADOTable1.Filter:=‘語文>60’;
ADOTable1.Filtered:=True;
這裏,因爲“語文”是數值類型字段,所以過濾條件就是:‘語文>60’。然而如果是文本類型的字段呢?情況稍微有些複雜。看下面一個例子:
例2‑6在Employee.mdb數據庫的Employee表中根據縣市查找記錄。
圖 2‑18
“過濾”命令按鈕的OnClick事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject);
var XianShi:string;
begin
adotable1.Filtered:=false;
end;
本例中,最難理解的一條語句是:
adotable1.Filter:='縣市='+''''+ XianShi +''''
爲什麼不能寫成adotable1.Filter:='縣市= XianShi' 呢? 因爲Filter屬性要求過濾條件必需是形如:
<字段名> <關係運算符> <常量>
的一個字符串,如:‘語文>60’,‘數學=80’ 等。但如果要求過濾條件是:
姓名=‘張三’
那麼,能不能寫成:
‘姓名=‘張三’’
呢?
答案是:不能,這樣寫會出現語法錯誤!因爲Delphi規定:如果單引號出現在字符串中,則必需要使用連續的兩個單引號代替一個單引號。所以,必需寫成:
‘姓名=‘‘張三’’’
或者也可以寫成:‘姓名=’+‘‘’’+‘張三’+‘‘’’
|
1.6.3 OnFilterRecord事件
將Filtered屬性值設爲True時,同時將會觸發
procedure TForm1.ADOTable1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
end;
在OnFilterRecord事件中有一個布爾值的變量Accept,只要將過濾條件賦值給該變量,當OnFilterRecord事件被觸發時,就會進行記錄篩選,將滿足條件的記錄顯示出來。
例如,可以將例2‑6改爲:
procedure TForm1.ADOTable1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
var XianShi:string;
begin
end;
而在“過濾”命令按鈕的OnClick事件處理程序如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
adotable1.Filtered:=false;
end;
最後再看一個例子:
例2‑7設計一個可按各個字段、各個篩選條件進行記錄過濾的程序,見圖 2‑19所示。
圖 2‑19按各字段、條件進行記錄過濾
本程序使用了兩個ComboBox組件來讓用戶選擇字段名稱與條件符號,還有一個輸入條件值的Edit組件,這三者構成了篩選記錄的條件式,用戶只要單擊“篩選”按鈕,即可篩選出表內符合條件的記錄。
窗體上“字段名稱”標籤後的ComboBox組件是用來選取表的字段,在程序開始時必須將表中的字段名加入到這個ComboBox組件中,所以在窗體的OnActive事件中編寫了如下語句:
procedure TForm1.FormActivate(Sender: TObject);
Var
I:Integer;
begin
for I:=0 to ADOTable1.Fields.Count-1 do
end;
另外,直接利用For循環將字段名逐一加到ComboBox中,其中ADOTable1.Fields.Count值表示表中字段的個數,因爲它的計數是從1開始,但是我們讀取字段名稱的數組卻是從0開始的。因此需要減1。
至於“條件符號”後的那個ComboBox組件,它的項目內容直接由Items屬性設置就可以了,共有:=、>、>=、<、<=、<> 6項。
選擇好字段名稱、條件符號並在條件值中輸入篩選的字段數據值後,當單擊“篩選”按鈕時,並不是進行篩選操作,而是進行篩選條件的變量指定,爲了讓我們指定的各總分篩選條件值能順利地傳遞給OnFilterRecord事件,應先聲明兩個全局變量來記錄字段名稱與條件值3個字段的內容,如圖?所示。
圖 2‑20聲明全局變量
然後在“篩選”按鈕的OnClick事件中指定變量爲各總分篩選條件值,在指定好變量值後,將Filtered屬性值設置爲True,觸發ADOTable1組件的OnFilterRecord事件,程序語句如下:
課外練習:
1、
A、一個字段對象
B、字段值
C、表
D、一條記錄
2、
A、字段值
B、字段對象
C、數據集
D、一條記錄
3、
A、Edit.text:=ADOTable1.fields[1].asString;
B、Edit.text:=ADOTable1.field(“姓名”);
C、Edit.text:=ADOTable1.fieldbyname(“姓名”);
D、Edit.text:=ADOTable1.ADOTable1.FieldValues(‘姓名’);
4、
A、指向首記錄
B、指向尾記錄
C、不改變記錄指針位置
D、指向找到的那條記錄
5、
A、首記錄
B、尾記錄
C、當前記錄
D、全部記錄
6、
A、一個屬性
B、一個事件
C、一個字段對象
D、一個方法
7、
A、ADOTable1.Locate( ‘XM’,‘張三’ ,[ ])
B、ADOTable1.Locate(‘張三’,‘XM’)
C、ADOTable1.Locate(XM,‘張三’,[ ])
D、ADOTable1.Locate(‘張三’,‘XM’,‘ ’)
1、
2、
3、
4、
5、
6、
7、