多層資料庫開發六:什麼是資料集

來源:互聯網
上載者:User
第六章 什麼是資料集
  Delphi 4中有四種類型的標準資料集構件,分別是TTable、TQuery、TStoredProc和TClientDataSet。這些資料集構件都是從一個共同的基類TDataSet繼承下來的,其中,只有TClientDataSet是直接從TDataSet繼承下來的,而TTable、TQuery、TStoredProc的直接上級是TDBDataSet,TDBDataSet的上級是TBDEDataSet,TBDEDataSet 的上級才是TDataSet。這幾個類之間的繼承關係可以用圖6.1來表示。
  圖6.1 資料集的繼承關係
  TDataSet是所有資料集的抽象基類,它的大部分屬性和方法是虛擬或抽象的。所謂虛擬方法,是指這些方法可以被衍生類別重載。所謂抽象的方法,是指這些方法只有聲明,沒有定義,衍生類別必須給出定義後才能調用這些方法,不同的衍生類別可以有不同的定義。
  由於TDataSet中包含抽象的方法,您不能直接建立它的執行個體,否則會引起運行期錯誤。
  如果從功能上劃分,TDataSet的屬性和方法可以分為這麼幾大塊:開啟和關閉資料集、瀏覽記錄、編輯資料、書籤管理、控制串連、訪問欄位、記錄緩衝區管理、過濾、事件。
6.1 開啟和關閉資料集
  在對資料集進行任何操作之前,首先要開啟資料集。要開啟資料集,可以把Active屬性設為True,例如:
  CustTable.Active := True;
  也可以調用Open函數,例如:CustQuery.Open;
  要關閉資料集,可以把Active屬性設為False或者調用Close函數。
6.2 資料集的狀態
  資料集的狀態(State屬性)決定了當前能夠對資料集進行的操作,例如,當資料集已經關閉,它的狀態是dsInactive,此時就不能訪問資料集的任何資料。
6.2.1 State屬性
  State屬性是唯讀,下面列出了State屬性可能的值:
.dsInactive 資料集已關閉,不能訪問它的資料;
.dsBrowse 資料集已開啟,可以瀏覽資料但不能修改資料;
.dsEdit 此時為編輯狀態,可以修改資料;
.dsInsert 此時可以插入一條新的記錄;
.dsSetKey 只適用於TTable和TClientDataSet,此時可以設定範圍和索引值,並且可以調用GotoKey函數;
.dsCalcFields 正在處理OnCalcFields事件,此時不能修改非計算欄位的值;
.dsCurValue 內部使用;
.dsNewValue 內部使用;
.dsOldValue 內部使用;
.dsFilter 進行中過濾操作。
  當一個資料集剛剛開啟的時候,它的State屬性被設為dsBrowse,以後,State屬性的值會隨著應用程式的操作自動變化。
  要使資料集進入dsBrowse、dsEdit、dsInsert或dsSetKey狀態,就得調用相應的方法。
   例如,要使資料集CustTable進入dsInsert狀態,程式樣本如下:
Procedure TForm1.InsertButtonClick(Sender: TObject);
Begin
CustTable.Insert;{進入dsInsert狀態}
AddressPromptDialog.ShowModal;
If AddressPromptDialog.ModalResult := mrOK then
CustTable.Post; {恢複為dsBrowse狀態}
Else
CustTable.Cancel; {恢複為dsBrowse狀態}
End;
  從上面這個例子可以看出,有些操作會使資料集自動變成dsBrowse狀態,例如,調用Post函數如果成功的話,資料集就恢複為dsBrowse狀態,如果調用Post沒有成功,資料集仍然保持原來的狀態。調用Cancel也能使資料集恢複為dsBrowse狀態。
  如果把Active屬性設為False,或者調用Close,將使資料集進入dsInactive狀態。例如,下面兩行代碼是等價的:
  CustTable.Active := False;
  CustTable.Close;
  有些狀態如dsCalcFields、dsCurValue、dsNewValue、dsOldValue和dsFilter不能被應用程式所控制,而是由資料集本身根據需要自動化佈建的。例如,當正在處理OnCalcFields事件時,就自動進入dsCalcFields狀態。當退出處理OnCalcFields事件的控制代碼時,資料集自動回復成原先的狀態。
  當資料集的狀態發生改變時,會觸發TDataSource構件的OnStateChange事件,如果這個TDataSource構件的DataSet屬性指定了這個資料集的話。
  下面詳細介紹資料集的各種狀態以及怎樣進入這些狀態。
6.2.2 dsInactive狀態
  當資料集已關閉時,就處於dsInactive狀態。此時,不能訪問它的任何資料。
  要使資料集進入dsInactive狀態,可以把Active屬性設為False,或者調用Close。在資料集將要關閉之前,會觸發BeforeClose事件。當資料集剛剛關閉,會觸發AfterClose事件。如果在資料集處於dsEdit或dsInsert狀態時調用Close,應當在處理BeforeClose事件的控制代碼中提示使用者是認可還是取消。程式樣本如下:
Procedure CustTable.VerifyBeforeClose(DataSet: TDataSet)
Begin
If (CustTable.State = dsEdit) or (CustTable.State = dsInsert) then
Begin
If MessageDlg('認可修改嗎?', mtConfirmation, mbYesNo, 0) = mrYes then
CustTable.Post;
ElseCustTable.Cancel;
End;
End;
6.2.3 dsBrowse狀態
  當一個資料集剛剛開啟的時候,資料集總是處於dsBrowse狀態,此時,可以顯示資料集中的記錄,但不能編輯和插入記錄。
  dsBrowse狀態可以認為是資料集的基本狀態,在此狀態下,可以進入其他狀態。例如,調用Insert或Append函數將使資料集的狀態從dsBrowse變成dsInsert(當然,這還取決於其他因素,如CanModify屬性的值),調用SetKey將使資料集從dsBrowse變成dsSetKey狀態。
  TDataSet有兩個方法可以使資料集回到dsBrowse狀態,一個是Cancel,它將取消當前進行中的編輯、插入、搜尋等操作,使資料集回到dsBrowse狀態。另一個是Post,它將試圖把修改了的資料儲存到資料集中,如果成功的話,資料集將回到dsBrowse狀態,如果沒有成功,資料集仍然保持原先的狀態。
6.2.4 dsEdit狀態
  如果應用程式要修改資料集的資料,必須首先進入dsEdit狀態。要進入dsEdit狀態,可以調用Edit。不過,調用Edit並不能保證一定能進入dsEdit狀態,這還取決於CanModify屬性的值。如果這個屬性返回True的話,表示資料集是可以讀和寫的。
  對於TTable構件來說,如果ReadOnly屬性設為True,CanModify屬性肯定返回False。對於TQuery構件來說,如果RequestLive屬性設為False,CanModify屬性肯定返回False。
  即使資料集進入了dsEdit狀態,也並不意味著使用者就一定能修改資料,資料控制項的ReadOnly屬性還必須設為False。此外,對於SQL資料庫來說,使用者能不能修改資料還取決於使用者有沒有修改資料的許可權。
  要從dsEdit狀態返回到dsBrowse狀態,可以調用Cancel、Post或Delete函數,其中,如果Post和Delete沒有調用成功的話,就仍然保持dsEdit狀態。
  在資料控制項中,當使用者修改了資料後把輸入焦點移走,就相當於調用Post函數,將使資料集回到dsBrowse狀態。
6.2.5 dsInsert狀態
  如果應用程式要插入新的記錄,必須首先進入dsInsert狀態。要進入dsInsert狀態,可以調用Insert或Append函數。不過,調用Insert或Append並不能保證一定能進入dsInsert狀態,這還取決於CanModify屬性的值是否返回True。
  即使資料集進入了dsInsert狀態,也並不意味著使用者就一定能插入記錄,資料控制項的ReadOnly屬性還必須設為False。此外,對於SQL資料庫來說,使用者能不能插入記錄還取決於使用者有沒有修改資料的許可權。
  要從dsInsert狀態返回到dsBrowse狀態,可以調用Cancel、Post或Delete函數,其中,如果Post沒有調用成功的話,就仍然保持dsInsert狀態。
  在資料控制項中,當使用者修改了資料後把輸入焦點移走,就相當於調用Post,將使資料集回到dsBrowse狀態。
6.2.6 dsSetKey狀態
  可以調用Locate、Lookup在資料集中搜尋特定的記錄。對於TTable構件來說,還可以調用GotoKey、GotoNearest、FindKey或FindNearest在表格中搜尋特定的記錄。在調用上述方法之前,必須首先使資料集進入dsSetKey狀態。要使資料集進入dsSetKey狀態,可以調用SetKey。調用了上述方法後,資料集又回到dsBrowse狀態。
  另外,可以對資料集進行過濾。對於TTable構件來說,還可以預先設定範圍。在進行過濾和範圍操作前,也要首先進入dsSetKey狀態。
6.2.7 dsCalcFields狀態
  當OnCalcFields事件被觸發時,就會使資料集進入dsCalcFields狀態。在處理OnCalcFields事件的控制代碼中,應當給出“計算欄位”的值。
  在dsCalcFields狀態下,除了“計算欄位”外,應用程式不能修改其他欄位的值,因為如果其他欄位的值發生變化,又會導致OnCalcFields事件被觸發,從而導致無限迴圈。
  當OnCalcFields事件處理完畢,資料集又回到dsBrowse狀態。
6.2.8 dsFilter狀態
  當OnFilterRecord事件被觸發時,就會使資料集進入dsFilter狀態。在此狀態下,不允許修改資料集的記錄,否則,過濾就無法正確執行。
  當OnFilterRecord事件處理完畢,資料集就回到dsBrowse狀態。
6.2.9 dsNewValue、dsOldValue或dsCurValue
  在允許緩衝更新的情況下,當使用者修改資料集的記錄時,資料集有可能會進入dsNewValue、dsOldValue或dsCurValue狀態。在這三種狀態下,可以通過欄位(TField)的NewValue、OldValue或CurValue屬性來訪問當時的值。
  上述三個狀態是由Delphi 4內部使用的,應用程式無法主動進入上述三種狀態。
6.3 瀏 覽 記 錄
  每個活動的資料集都有一個指標,指向目前記錄。很多對資料集的操作都是針對目前記錄,許多資料控制項也只顯示目前記錄的資料,因此,在資料庫應用程式中知道目前記錄的位置是非常重要的。
  資料庫應用程式往往要改變目前記錄的位置,這時候就要用到下面這些方法:
.First 使第一條記錄成為目前記錄;
.Last 使最後一條記錄成為目前記錄;
.Next 使下一條記錄成為目前記錄;
.Prior 使前一條記錄成為目前記錄;
.MoveBy 使距離目前記錄若干行的記錄成為目前記錄。
  此外,Delphi 4中有一個TDBNavigator構件,專門用來瀏覽記錄,它把上述方法用按鈕來實現。
  除了上述方法外,TDataSet中還有兩個唯讀布爾類型的屬性用來判斷目前記錄的位置,一個是Bof,如果這個屬性返回True,表示現在已到了資料集的開始位置。另一個是Eof,如果這個屬性返回True,表示現在已到了資料集的末尾。
6.3.1 First和Last
  調用First函數能夠使資料集的第一條記錄成為目前記錄,並且把Bof屬性設為True。如果第一條記錄已經是目前記錄了,First就什麼也不幹。程式樣本如下:
  CustTable.First;
  調用Last函數能夠使資料集的最後一條記錄成為目前記錄,並且把Eof屬性設為True。如果最後一條記錄已經是目前記錄,Last就什麼也不幹。程式樣本如下:
  CustTable.Last;
  用TDBNavigator構件實現的導航器上有兩個按鈕,分別對應著First和Last。
6.3.2 Next和Prior
  調用Next函數能夠使下一條記錄成為目前記錄。如果目前記錄已經是資料集的最後一條記錄,Next就什麼也不幹。程式樣本如下:
  CustTable.Next;
  調用Prior能夠使前一條記錄成為目前記錄。如果目前記錄已經是資料集的第一條記錄,Prior就什麼也不幹。程式樣本如下:
  CustTable.Prior;
6.3.3 MoveBy
  調用MoveBy函數使資料集中的另一條記錄成為目前記錄,該記錄距原先的目前記錄若干行。MoveBy需要傳遞一個參數,指定相隔的行數,正數表示向記錄編號增大的方向移動,負數表示向記錄編號減小的方向移動。程式樣本如下:
  CustTable.MoveBy(-2);
  MoveBy返回實際移動的行數,傳回值與傳遞給MoveBy的參數有可能不同。
  注意:在多使用者環境下,其他使用者有可能正在修改、插入或刪除記錄,這樣,一條記錄原來距目前記錄是5行,現在有可能變為4行和6行,甚至該記錄都不存在了,因為其他使用者修改該記錄的資料或刪除了該記錄。
6.3.4 Eof和Bof屬性
  TDataSet有兩個唯讀屬性Eof和Bof,分別用於判斷是否到了資料集的末尾和開頭。在遍曆資料集的所有記錄時經常要用到這兩個屬性。
  如果Eof屬性返回True,表示現在已到了資料集的末尾。
  進行下列操作時會把Eof屬性設為True:
.開啟一個空的資料集;
.調用了Last;
.調用了Next,而現在已經在資料集的最後一條記錄;
.調用了SetRange,而範圍是無效的。
  除了上述情況外,Eof屬性都將返回False。
  Eof屬性通常用在一個迴圈中,每調用一次Next就要判斷一下Eof屬性,以避免對不存在的記錄進行操作。程式樣本如下:
CustTable.DisableControls;
Try
CustTable.First;
While not CustTable.EOF Do
Begin
...
CustTable.Next;
End;
Finally
CustTable.EnableControls;
End;
上述代碼同時示範了在遍曆資料集的所有記錄時怎樣暫時禁止資料控制項跟著重新整理。在遍曆資料集的所有記錄前應當調用DisableControls禁止重新整理,這樣能夠加快遍曆的速度,因為重新整理也是要花時間的。遍曆結束後,應當調用EnableControls恢複重新整理。
  EnableControls最好在Try...Finally結構的Finally部分調用,這樣能保證即使在遍曆時出現異常,也能保證重新整理能得到恢複。
  如果Bof屬性返回True,表示現在已到了資料集的開頭。
  進行下列操作時會把Bof屬性設為True:
.開啟一個非空的資料集;
.調用了First;
.調用了Prior,而現在已經在資料集的第一條記錄。
  除了上述情況外,Bof屬性都將返回False。與Eof屬性一樣,Bof屬性通常用在一個迴圈中,每調用一次Prior就要判斷一下Bof屬性,以避免對不存在的記錄進行操作。程式樣本如下:
CustTable.DisableControls;
Try
While not CustTable.BOF Do
Begin
...
CustTable.Prior;
End;
Finally
CustTable.EnableControls;
End;
6.4 書 簽
  書籤的作用是在資料集的某個位置做一個標記,以後可以快速方便地回到那個位置。TDataSet中提供了若干個屬性和方法用於管理書籤。
  如果讀Bookmark屬性,返回目前記錄的書籤。如果寫Bookmark屬性,它能使一個指定的書籤成為當前書籤。
  TDataSet中有關書籤的幾個函數都是虛擬,TDataSet的衍生類別TBDEDataSet重新定義了這些方法,包括:
.BookmarkValid判斷某個書籤是否合法;
.CompareBookmarks比較兩個書籤是否相同;
.GetBookmark建立一個書籤來標記目前記錄;
.GotoBookmark回到用GetBookmark標記的位置;
.FreeBookmark刪除一個書籤。
  要建立一個書籤,首先要聲明一個TBookmark類型的變數,然後調用GetBookmark函數建立一個標記目前記錄的書籤。TBookmark類型的變數實際上是一個無類型的指標。
  在調用GotoBookmark之前,最好先調用BookmarkValid判斷書籤是否合法,因為書籤標記的記錄有可能已刪掉。如果BookmarkValid返回True,說明書籤是合法的,可以調用GotoBookmark跳轉到書籤標記的位置。
可以調用CompareBookmarks比較兩個書籤是否相同,如果兩個書籤不同,這個函數就返回1。如果兩個書籤相同或者都是NIL,這個函數就返回0。
  GotoBookmark需要傳遞一個參數,即書籤。
  FreeBookmark用於刪除一個書籤。當一個書籤已用不到時,應當及時刪除它,因為書籤也是一種資源。
  下面這段代碼示範了書籤的用法:
Procedure DoSomething (const Tbl: TTable)varBookmark: TBookmark;
Begin
Bookmark := Tbl.GetBookmark;
Tbl.DisableControls;
Try
Tbl.First;
While not Tbl.EOF Do
Begin
...
Tbl.Next;
End;
Finally
Tbl.GotoBookmark(Bookmark);
Tbl.EnableControls;
Tbl.FreeBookmark(Bookmark);
End;
End;
6.5 搜尋特定的記錄
  可以調用Locate和Lookup函數在資料集中搜尋特定的記錄。
   Locate用於在資料集中定位一條特定的記錄,並使該記錄成為目前記錄。Locate需要傳遞三個參數,第一個是KeyFields參數,用於指定要按哪些欄位搜尋,第二個是KeyValues參數,用於指定每個欄位相應的值,第三個是Options參數,用於設定搜尋選項。
  下面這個例子搜尋Company欄位的值為“Professional Ltd.”的記錄:
var
LocateSuccess: Boolean;
SearchOptions: TLocateOptions;
Begin
SearchOptions := [loPartialKey];
LocateSuccess := CustTable.Locate('Company', 'Professional Ltd.',SearchOptions);
End;
  如果Locate找到了一條合格記錄,就把該記錄變為目前記錄,並返回True。如果Locate沒有找到匹配的記錄,就返回False。
  對於Locate來說,KeyFields參數指定的欄位越多,搜尋的條件就越精確。如果KeyFields參數需要指定多個欄位,彼此之間要用分號分開。由於欄位的資料類型可能各不相同,因此,KeyValues是一個Variant類型的參數。如果KeyFields參數指定了多個欄位,KeyValues參數必須是一個可變類型的數組。程式樣本如下:
  With CustTable Do
   Locate('Company;Contact;Phone', VarArrayOf(['Sight Diver','P']), loPartialKey);
   Lookup與Locate非常相似,也是在資料集中搜尋特定的記錄。不同的是,如果找到匹配的記錄,Lookup能返回該記錄中若干個欄位的值。
   Lookup需要傳遞三個參數,第一個是KeyFields參數,用於指定要按哪些欄位搜尋,第二個是KeyValues參數,用於指定每個欄位相應的值,第三個是ResultFields參數,用於指定要返回哪些欄位的值。
  下面這個例子在CustTable中搜尋Company欄位的值為“Professional Ltd.”的記錄,並返回Company、Contact、Phone等欄位的值:
var
LookupResults: Variant;
Begin
With CustTable Do
LookupResults := Lookup('Company', 'Professional Divers, Ltd.', 'Company;Contact; Phone');
End;
  如果ResultFields參數指定了多個欄位,Lookup返回一個可變類型的數組。如果沒有找到匹配的記錄,Lookup將返回一個空的數組。程式樣本如下:
var
LookupResults: Variant;
Begin
With CustTable Do
LookupResults := Lookup('Company; City', VarArrayOf(['Sight Diver',
  'Christiansted']), 'Company; Addr1; Addr2; State; Zip');
End;
6.6 過 濾
  一個應用程式往往只對資料集的部分記錄感興趣,例如,可能只對一個客戶表中來自廣東的客戶感興趣。這種情況下,可以用過濾技術把符合特定條件的記錄過濾出來。
  不過,對於一個欄位很多的資料集來說,最好還是使用查詢。
6.6.1 允許過濾
  要對資料集進行過濾,首先要指定過濾條件,並設定FilterOptions屬性設定有關選項(可選),然後把Filtered屬性設為True。以後如果不想進行過濾,只要把Filtered屬性設為False。
  要指定過濾條件有兩種方式:一是設定Filter屬性,二是在處理OnFilterRecord事件的控制代碼中給出過濾條件。
  Filter屬性適合於在運行期使用,它能夠動態地指定過濾條件,能夠根據需要改變過濾條件。不過,Filter屬性是一個字串,過濾條件相對比較簡單。雖然可以用運算子構成複合的條件運算式,但只限於幾個常見的運算子。更主要的是,用Filter屬性指定的運算式中只能出現資料集中已有的欄位名和常量,不能出現其他資料。
  而OnFilterRecord事件則靈活得多,它能夠在設計期就指定好過濾條件。而在處理OnFilterRecord事件的控制代碼中,可以任意指定過濾條件,過濾條件可以很複雜。
6.6.2 Filter屬性
  Filter屬性是一個字串,可以這樣設定Filter屬性:
  Dataset1.Filter := '''State'' = ''CA''';
  也可以這樣設定Filter屬性:
  Dataset1.Filter := Edit1.Text;
  上面這行代碼允許讓使用者自己輸入過濾條件。甚至還可以把上述兩行代碼結合起來:
  Dataset1.Filter := '''State'' = ' + Edit1.Text;
  設定了過濾條件後,只要把Filtered屬性設為True,過濾即有效。
  可以用比較和邏輯運算子構造複合的過濾條件,這些運算子包括:
. <小於;
. >大於;
. >=大於等於;
. <=小於等於;
.=等於;
. <>不等於;
.AND兩邊的運算式都必須為True;
.NOT運算式不能為True;
.OR兩個運算式只要有一個為True。
  下面這個例子用AND運算子限制CustNo欄位必須大於1400且小於1500:
  (CustNo > 1400) AND (CustNo < 1500);
  注意:在Filtered屬性設為True的情況下,使用者修改、插入的記錄有可能不符合過濾的條件,這時候,會拒絕接受與過濾條件矛盾的記錄。
6.6.3 OnFilterRecord事件
  在Filtered屬性設為True的情況下,資料集中的每條記錄都會觸發OnFilterRecord事件,這樣,就有機會決定是否要把記錄過濾。
  處理OnFilterRecord事件的控制代碼中有一個布爾類型的Accept參數,把這個參數設為True表示接受此記錄,把這個參數設為False表示把此記錄過濾掉。程式樣本如下:
Procedure TForm1.Table1FilterRecord(DataSet: TDataSet;
var Accept: Boolean);
Begin
Accept := DataSet['State'] = 'CA';
End;
  上面這個例子的意思是,只有State欄位的值為CA的記錄才被接受。
  注意:由於資料集的每條記錄都會觸發OnFilterRecord事件,因此,處理OnFilterRecord事件的代碼要儘可能地簡短,尤其是對一個有很多條記錄的大型資料集。
  有時候,程式需要按多種不同的過濾條件進行過濾,可以建立多個處理OnFilterRecord事件的控制代碼,然後在運行期動態地切換事件控制代碼,程式樣本如下:
  DataSet1.OnFilterRecord := NewYorkFilter;Refresh;
6.6.4 設定過濾選項
  FilterOptions屬性用於設定過濾的選項。這個屬性是一個集合,可以是空集(預設),也可以包含下列元素:
.foCaseInsensitive比較字串時忽略大小寫;
.foPartialCompare對於字串類型的欄位必須全字匹配,不允許部分匹配。
  例如,為了在比較State欄位時忽略大小寫,可以這樣設定:
  FilterOptions := [foCaseInsensitive];
  Filter := '''State'' = ''CA''';
6.6.5 在過濾後的資料集中瀏覽記錄
  過濾後的資料集實際上是原來的資料集的一個子集。TDataSet提供了四個方法用於在過濾後的資料集中瀏覽記錄,它們是:
.FindFirst使過濾後的資料集中的第一條記錄成為目前記錄;
.FindLast使過濾後的資料集中的最後一條記錄成為目前記錄;
.FindNext使過濾後的資料集中的下一條記錄成為目前記錄;
.FindPrior使過濾後的資料集中的前一條記錄成為目前記錄。
  上述四個方法如果調用成功,就返回True,否則,就返回False。可以檢查一個唯讀Found屬性,看看上次調用是否成功。
  如果通過Filter屬性或OnFilterRecord事件設定了過濾條件,而Filtered屬性設為False,調用上述四個方法時會自動暫時允許過濾,然後移動目前記錄的位置,最後又禁止過濾。換句話說,上述四個方法可以不理會Filtered屬性是怎樣設定的。
  如果沒有設定過濾條件,上述四個方法即相當於First、Last、Next和Prior。
6.7 修 改 數 據
  TDataSet中提供了一些方法用於在資料集中更新、插入和刪除記錄,它們是:
.Edit使資料集進入dsEdit狀態;
.Append在資料集的末尾添加一條記錄;
.Insert在資料集的當前位置插入一條記錄;
.Post試圖把使用者對資料的修改寫到資料集中;
.Cancel取消使用者對資料的修改,使資料集回到dsBrowse狀態;
.Delete刪除目前記錄。
6.7.1 進入dsEdit狀態
  要編輯資料集的記錄,首先要進入dsEdit狀態。要進入dsEdit狀態,調用Edit函數。不過,調用Edit不一定會使資料集進入dsEdit狀態,還取決於CanModify屬性的值。
  一旦資料集進入了dsEdit狀態,使用者就可以在資料控制項上修改目前記錄的值。當使用者把輸入焦點從目前記錄上移走,即相當於調用了Post函數。程式樣本如下:
With CustTable Do
Begin
Edit;
FieldValues['CustNo'] := 1234;
Post;
End;
  要取消當前未決的修改,使用者可以按ESC鍵或單擊用TDBNavigator構件實現的導航器上的Cancel按鈕。
  在使用緩衝更新技術(CachedUpdates屬性設為True)的情況下,調用Post只是把資料寫到緩衝中,而不是直接寫到資料集中。要把緩衝中的資料寫到資料集中,需調用ApplyUpdates函數。
6.7.2 插入新的記錄
  要在資料集中插入新的記錄,首先要進入dsInsert狀態。要進入dsInsert狀態,可以調用Insert或Append函數。不過,調用Insert或Append不一定會使資料集進入dsInsert狀態,還取決於CanModify屬性的值。
  一旦進入了dsInsert狀態,使用者就可以在資料控制項(一般是TDBGrid)中插入一條新的記錄,並給這條記錄輸入資料。
  如果要通過編程來插入新的記錄,就要注意Insert和Append的區別。Insert將把一條新的記錄插入到目前記錄的前面,而Append將把一條新的記錄添加到資料集的末尾。
  插入了新的記錄後,應當調用Post或在CachedUpdates屬性設為True的情況下調用ApplyUpdates把新的記錄寫到資料集中。
  如果資料集是已建立了索引的Paradox或dBASE表,新記錄將自動移到恰當的位置。
  如果資料集沒有建立索引,新記錄就插入到資料集的當前位置(Insert)或末尾(Append)。
6.7.3 刪除記錄
  調用Delete函數將刪除目前記錄,並且使資料集回到dsBrowse狀態。如果表單上有TDBNavigator構件的話,使用者可以單擊導航器上的“Delete”按鈕刪除目前記錄。目前記錄被刪除後,下一條記錄就成為目前記錄。
  如果刪除的本來就是最後一條記錄,則前一條記錄成為目前記錄。
6.7.4 修改整條記錄
  除了TDBGrid和TDBNavigator外,大部分資料控制項只能工作於資料集的一個或幾個欄位,而不是整條記錄。
  不過,TDataSet提供了若干個方法可以直接修改整條記錄而不是單獨的欄位,這些方法包括:
.AppendRecord類似於Append,但可以給欄位賦值,不需要調用Post;
.InsertRecord類似於Insert,但可以給欄位賦值,不需要調用Post;
.SetFields對目前記錄的欄位賦值,需要顯式地調用Post。
  上述三個方法都要傳遞一個TVarRec類型的數組作為參數,該數組的每一個元素對應著一個欄位的值。如果數組的元素個數小於資料集的欄位個數,剩下欄位的值就是NULL。
  對於沒有建立索引的資料集來說,AppendRecord把一條新的記錄加到資料集的末尾。對於已建立索引的資料集來說,新記錄將自動移到一個恰當的位置。
  SetFields用於對目前記錄的欄位賦值。在調用SetFields之前,首先要調用Edit,使資料集進入dsEdit狀態。調用了SetFields後,需要顯式地調用Post函數。
  調用SetFields時,如果您只想對部分欄位賦值,讓其他欄位的值保持不變,可以用NULL或NIL去賦值。
  假設一個資料集中有五個欄位,分別是Name、Capital、Continent、Area和Population,可以這樣對它們賦值:
CountryTable.InsertRecord(['Japan', 'Tokyo', 'Asia']);
  上述程式在資料集中插入了一條新的記錄,並且對前三個欄位賦了值。現在可以再次對目前記錄賦值,不過,這次只想對Area欄位和Population欄位賦值,程式就要這樣寫:
With CountryTable Do
Begin
If Locate('Name', 'Japan', loCaseInsensitive) then
Begin
Edit;
SetFields(NIL, NIL, NIL, 344567, 164700000);
Post;
End;
End;
  注意:此處要用NIL而不是NULL,否則,前三個欄位會被設為空白。
6.8 事 件
  TDataSet的事件主要分為兩大類,一類是Before系列,另一類是After系列,列表如下:
.BeforeOpen,AfterOpen發生在資料集開啟前後;
.BeforeClose,AfterClose發生在資料集關閉前後;
.BeforeInsert,AfterInsert發生在插入了一條新的記錄前後;
.BeforeEdit,AfterEdit 發生在進入dsEdit狀態前後;
.BeforePost,AfterPost 發生在寫資料集的前後;
.BeforeCancel,AfterCancel發生在取消修改的前後;
.BeforeDelete,AfterDelete發生在刪除記錄的前後。
  此外,當資料集中增加了一條新的記錄時就會觸發OnNewRecord事件,當“計算欄位”的值需要重算時將觸發OnCalcFields事件。
  Before系列的事件常常用來中止操作。例如,當調用Delete函數試圖刪除目前記錄時,在目前記錄將要刪除前會觸發BeforeDelete事件,可以在處理BeforeDelete事件的控制代碼中調用Abort或觸發一個異常放棄刪除目前記錄,程式樣本如下:
Pocedure TForm1.TableBeforeDelete (Dataset: TDataset)
Begin
If MessageDlg('Delete This Record?', mtConfirmation, mbYesNoCancel, 0) <> mrYes Then Abort;
End;
  After系列的事件往往用來在狀態列上通知使用者,程式樣本如下:
Procedure TForm1.Table1AfterDelete(DataSet: TDataSet);
Begin
StatusBar1.SimpleText := Format('有%d 條記錄',[DataSet.RecordCount]);
End;
  OnCalcFields事件主要用於給出“計算欄位”的值。AutoCalcFields屬性的值決定了什麼時候會發生OnCalcFields事件。
  如果AutoCalcFields屬性設為True,下列情況下會發生OnCalcFields事件:
.資料集被開啟時;
.在資料控制項中,輸入焦點從一條記錄移到另一條記錄;
.在資料控制項中,輸入焦點從一個欄位移到另一個欄位;
.目前記錄被修改或從資料庫中檢索了一條記錄。
  不過,即使AutoCalcFields屬性設為False,當資料集中的任意一個非計算欄位的值發生變化時都會觸發OnCalcFields事件。
  由於OnCalcFields事件有可能是頻繁發生的,因此,處理OnCalcFields 事件的代碼要儘可能地簡短。在AutoCalcFields屬性設為True的情況下,在處理OnCalcFields事件的控制代碼中不能修改資料集的資料,因為一旦目前記錄被修改,又要觸發OnCalcFields事件,從而導致無限迴圈。例如,假設您在處理OnCalcFields事件的控制代碼中調用了Post,就會觸發OnCalcFields事件,導致再次調用Post,再次觸發OnCalcFields事件……
6.9 TBDEDataSet
  TBDEDataSet是從TDataSet繼承下來的,它提供了通過BDE(BorlandDatabase Engine)訪問資料的能力。這一節主要介紹TBDEDataSet,讀者應當對前面介紹的TDataSet已經有了比較深刻的認識。
  與TDataSet一樣,TBDEDataSet也是虛擬和抽象的,除非您想建立自訂的資料集,否則,一般不需要直接用到TBDEDataSet。
  TBDEDataSet重載了TDataSet中涉及記錄導航、索引和書籤的方法,增加了一些處理BLOB欄位、緩衝更新的屬性、方法和事件。
6.9.1 CacheBlobs屬性
  TBDEDataSet的CacheBlobs屬性用於控制BDE是否把BLOB欄位的內容放到緩衝中。如果這個屬性設為True,當應用程式讀取BLOB欄位的值時,BDE將把BLOB欄位的內容放在緩衝中,這樣,當應用程式下次要讀取這個欄位的值時,就不必再從資料庫伺服器那兒去檢索,只要直接從記憶體中取過來就行了,這樣可以提高應用程式的效能。
  不過,如果應用程式需要頻繁地更新BLOB欄位的值,這時候反而應當把CacheBlobs屬性設為False,這樣能保證檢索到的BLOB欄位的值總是最新的。
6.9.2 緩衝更新
  TBDEDataSet提供了緩衝更新的技術。所謂緩衝更新就是,應用程式從資料庫中檢索資料,在本機快取中建立一個副本,使用者對資料進行修改後,也只是反映在緩衝中,以後可以調用ApplyUpdates一次性地把所有的修改反映到資料集中。
  可以看出,緩衝更新技術可以明顯地提高應用程式的效能,而且可以方便地取消修改,只要還沒有調用ApplyUpdates。下面列出了TBDEDataSet中有關緩衝更新的屬性、方法和事件:
.CachedUpdates如果這個屬性設為True,緩衝更新有效;
.UpdateObject用於指定一個TUpdateSQL構件來更新基於查詢的資料集;
.UpdatePending如果緩衝中有未決的記錄,這個屬性就返回True;
.UpdateRecordTypes指定資料集中哪些記錄是可見的;
.UpdateStatus返回當前的更新狀態;
.OnUpdateError如果更新過程中出錯將觸發這個事件;
.OnUpdateRecord每更新一條記錄就會觸發一次這個事件;
.ApplyUpdates把緩衝中的資料寫到資料集中;
.CancelUpdates把緩衝中未決的修改取消;
.CommitUpdates把緩衝清掉;l FetchAll從資料庫檢索所有記錄到緩衝中;
.RevertRecord撤消對目前記錄的修改。
6.10 TDBDataSet
  TDBDataSet是從TBDEDataSet繼承下來的,它提供了資料庫和會話期管理的能力。
  TDBDataSet中增加了若干個屬性和方法用於管理資料庫和BDE會話期,包括:
.CheckOpen檢查資料庫是否已開啟;
.Database返回一個TDatabase構件;
.DBHandle返回一個BDE控制代碼,調用BDE的API時要用到這個控制代碼;
.DBLocale返回當前的國際語言驅動程式;
.DBSession返回一個BDE會話期對象;
.DatabaseName用於指定要訪問的資料庫;
.SessionName用於指定一個BDE會話期對象。
  這裡詳細解釋一下DatabaseName屬性和SessionName屬性。如果應用程式要訪問遠端資料庫伺服器如Sybase、Oracle或InterBase,應當用TDatabase構件來串連資料庫,此時,應當設定DatabaseName屬性指定要串連的資料庫,可以設為TDatabase構件的名稱。如果沒有顯式地使用TDatabase構件,DatabaseName屬性應當設為BDE 別名。對於Paradox和dBASE表來說,可以設為表的路徑。
  SessionName屬性用於指定一個BDE會話期對象。如果應用程式沒有顯式地使用TSession構件,不必設定這個屬性。如果應用程式顯式地使用了多個TSession構件,應當設定SessionName屬性指定其中一個。
  一般來說,應用程式用不到DBHandle、DBLocale和DBSession等屬性,除非要直接調用BDE的API。這三個屬性都是唯讀。
  TDBDataSet中還有一個唯讀Provider屬性,它能夠返回一個IProvider介面。在多層的Client/Server應用程式中,客戶程式需要通過IProvider介面與應用伺服器通訊。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.