天天看點

多層資料庫開發四:BDE會話期

                                              第四章 BDE會話期

  不管是單層、兩層還是多層的資料庫應用程式,一般都要用到BDE(BorlandDatabase Engine)。Delphi 4用TSession來管理BDE會話期對象,用TSessionList來管理和操縱一個應用程式中所有的BDE會話期對象。

  一般來說,并不需要顯式地把TSession構件放到窗體或資料子產品上,因為資料庫應用程式在每次啟動時會自動建立一個預設的BDE會話期對象叫Session。但如果開發的是一個多線程的資料庫應用程式,就要顯式地用到TSession構件,因為幾個線程有可能要同時連接配接資料庫,應當避免每次連接配接資料庫時都要建立應用程式的另一個執行個體。

<b>4.1 TSession</b>

  TSession構件能夠對一個應用程式内的一組TDatabase構件提供全局控制。當建立資料庫應用程式(包括應用伺服器)時,應用程式會自動建立一個預設的BDE會話期對象叫Session。在應用程式中加入新的TDatabase構件和新的資料集構件時,這些構件會自動地處于預設的BDE會話期對象即Session的控制之下。此外,BDE會話期對象還能夠提供通路Paradox表的密碼,指定網絡控制檔案所在的目錄,控制資料庫的連接配接。

  除了預設的BDE會話期對象外,有些應用程式需要用到另外的TSession構件。例如,如果應用程式要并行查詢同一個資料庫,此時,每一個查詢都必須有單獨的BDE會話期。多線程的資料庫應用程式也需要有多個BDE會話期。

  您既可以在設計期加入TSession構件,也可以在運作期動态地建立TSession構件。

<b>4.1.1 預設的BDE會話期對象</b>

  所有的資料庫應用程式都會自動包含一個預設的BDE會話期對象叫Session,它的SessionName屬性是Default。預設的BDE會話期對象能夠對所有的TDatabase構件提供全局控制,不管這些TDatabase構件是在設計期顯式地加到窗體或資料子產品上的還是在運作期動态建立的。注意:預設的BDE會話期對象在設計期是不存在的,它隻存在于運作期。

  無論是在設計期還是在運作期加入TDatabase構件時,加入的TDatabase構件都會自動處于預設的BDE會話期對象管理之下。當然,如果您顯式地使用了多個TSession構件,您也可以設定TDatabase構件的SessionName屬性指定其中一個TSession構件。

  TSession最重要的屬性是KeepConnections,如果這個屬性設為True,即使目前沒有打開資料集,也保持與資料庫的連接配接。這樣,下次打開資料集時,就不必再登入資料庫。

  注意:應用程式千萬不要試圖删除預設的BDE會話期對象。

<b>4.1.2 建立另外的BDE會話期對象</b>

  有些情況下需要用到另外的BDE會話期對象。在設計期,您可以把一個或多個TSession構件加到窗體或資料子產品上,然後在對象觀察器中設定它們的屬性,建立事件句柄,調用它們的方法。您也可以在運作期動态地建立BDE會話期對象。

  要在運作期動态地建立BDE會話期對象,可以參照下列步驟:

  第一步是聲明一個TSession類型的變量。

  第二步是調用TSession的Create建立一個TSession的對象執行個體。Create會自動把Databases數組清為空,把KeepConnections屬性設為True,同時還把新建立的BDE會話期對象加到由Sessions管理的BDE會話期對象的清單中。

  第三步是設定SessionName屬性指定此BDE會話期對象的名稱,注意不能與其他BDE會話期名稱相同。TDatabase和資料集構件都是通過名稱來區分BDE會話期對象。

  第四步是激活此BDE會話期對象,然後設定有關屬性。

  下面的程式示例建立了一個BDE會話期對象,最後又删掉了這個BDE會話期對象。

var SecondSession: TSession;

Begin

SecondSession := TSession.Create;

With SecondSession Do

Try

SessionName := 'SecondSession';

KeepConnections := False;

Open;

...

Finally

SecondSession.Free;

End;

  實際上,也可以調用TSessionList的OpenSession函數來建立BDE會話期對象,這個函數需要傳遞一個SessionName參數,指定要建立的BDE會話期對象的名稱。如果與SessionName參數比對的BDE會話期對象已存在,這個函數就激活它。程式示例如下:

Var MySession : TSession;

MySession := Sessions.OpenSession('MySession');

MySession.Free;

<b>4.1.3 給BDE會話期對象命名</b>

  TSession的SessionName屬性用于給BDE會話期對象命名。對于預設的BDE會話期對象來說,它的SessionName屬性是“Default”。

  在同一個應用程式内,BDE會話期對象的名稱不能有重複。如果把TSession的AutoSessionName屬性設為True,Delphi 4将自動為每個BDE會話期對象指定一個相異的名稱,這樣就不用擔心重名。如果AutoSessionName屬性設為False,應用程式就必須設定SessionName屬性給每個BDE會話期對象指定一個相異的名稱。在AutoSessionName屬性設為True的情況下,不能直接修改SessionName屬性的值。

  不過,使用AutoSessionName屬性有許多限制。例如,如果窗體或資料子產品上有多個TSession構件,就不能把AutoSessionName屬性設為True。如果窗體或資料子產品上已經有一個TSession構件,并且它的AutoSessionName屬性設為True,就不能再把另一個TSession構件加到窗體或資料子產品上。

  TDatabase構件和資料集構件都有一個SessionName屬性,用于指定一個BDE會話期對象的名稱。如果它們的SessionName屬性為空,表示使用預設的BDE會話期對象。

  下面這個例子首先調用TSessionList的OpenSession建立一個BDE會話期對象,然後設定Database1的SessionName屬性指定剛建立的BDE會話期對象。

var

IBSession: TSession;

IBSession := Sessions.OpenSession('InterBaseSession');

Database1.SessionName := 'InterBaseSession';

<b>4.1.4 激活BDE會話期對象</b>

  如果TSession的Active屬性傳回True,表示BDE會話期對象處于活動狀态。如果把這個屬性設為True,将激活BDE會話期對象,并觸發OnStartup事件。

  激活了BDE會話期對象後,就可以調用OpenDatabase函數來連接配接資料庫。

  把Active屬性設為True,相當于調用Open。把Active屬性設為False,相當于調用Close。

  對于預設的BDE會話期對象即Session,最好不要把它的Active屬性設為False。

  當BDE會話期對象被激活時将觸發OnStartup事件,這樣就有機會設定NetFileDir、PrivateDir和ConfigMode等屬性,不過,NetFileDir和PrivateDir屬性隻在通路Paradox表才是有效的。ConfigMode屬性用于設定哪些BDE别名是可見的。

<b>4.1.5 KeepConnections屬性</b>

  如果KeepConnections屬性設為True,即使目前沒有打開任何資料集,也保持與資料庫的連接配接。如果這個屬性設為False,當所有的資料集都關閉時,就斷開與資料庫的連接配接。

  這個屬性是針對動态生成的臨時的TDatabase構件而言的,如果顯式地使用了TDatabase構件,将以它的KeepConnections屬性為準。

  如果一個應用程式需要頻繁地打開和關閉所有的資料集,特别是當這些資料集是在一個遠端數伺服器上時,最好把KeepConnections屬性設為True,這樣可以避免再次登入到遠端伺服器。否則,應用程式不得不重新登入。

  不過,即使在KeepConnections屬性設為True的情況下,仍然可以調用DropConnections函數把空的連接配接斷開。所謂空的連接配接,是指目前并沒有打開任何資料集,但由于KeepConnections屬性設為True,仍然保持在連接配接狀态。

<b>4.1.6 打開和斷開連接配接</b>

  要打開一個資料庫的連接配接,調用OpenDatabase函數。這個函數需要傳遞一個DatabaseName參數,用于指定要打開的資料庫的名稱,可以設為BDE别名或TDatabase構件的名稱。對于Paradox或dBASE,DatabaseName參數也可以設為表所在的路徑。

  下面的程式示例試圖打開别名為DBDEMOS的資料庫:

DBDemosDatabase: TDatabase;

DBDemosDatabase := Session.OpenDatabase('DBDEMOS');

  OpenDatabase會首先自動激活BDE會話期(如果還沒有激活的話),然後判斷DatabaseName參數是否與BDE會話期對象所管理的TDatabase構件的名稱比對,如果沒有找到比對的資料庫,OpenDatabase就會自動建立一個臨時的TDatabase構件。最後,OpenDatabase調用TDatabase的Open連接配接資料庫。

  OpenDatabase實際上是使一個内部的計數加1,隻要這個計數大于0,資料庫就處于連接配接狀态。

  可以調用CloseDatabase函數來關閉一個資料庫。與OpenDatabase一樣,CloseDatabase也需要傳遞一個DatabaseName參數來指定要關閉的資料庫,例如:

  Session.CloseDatabase(DBDemosDatabase);

  CloseDatabase實際上是使一個内部的引用計數減1,當引用計數減到0時,資料庫才被關閉。

  如果DatabaseName參數指定的資料庫是由一個臨時的TDatabase構件管理的,而且KeepConnections屬性設為False,當資料庫被關閉的同時,相應的TDatabase構件也被删除。

  在KeepConnections屬性設為False的情況下,對于那些臨時建立的TDatabase構件來說,如果目前沒有打開任何資料集,資料庫将自動關閉,TDatabase構件将自動删除。對于那些顯式加到窗體或資料子產品上的TDatabase構件來說,需要調用Close才能關閉資料庫。

  如果要一次關閉所有的資料庫,可以把BDE會話期對象的Active屬性設為False,或者調用Close函數。把Active屬性設為False,會自動調用Close,而Close會關閉所有的資料庫,删除所有臨時建立的TDatabase構件,然後依次調用那些顯式使用的TDatabase的Close,最後,把BDE會話期的句柄設為NIL。

  在打開和關閉資料庫之前,可能需要調用FindDatabase函數來查找一個特定的資料庫是否存在。FindDatabase函數需要傳遞一個DatabaseName參數,用于指定要查找的資料庫,可以設為BDE别名或者TDatabase構件的名稱。對于Paradox或dBASE表來說,可以設為表所在的路徑。如果找到的話,FindDatabase函數就傳回一個TDatabase構件,否則,就傳回NIL。下面這個例子試圖搜尋别名為DBDEMOS的資料庫:

DB: TDatabase;

DB := Session.FindDatabase('DBDEMOS');

If (DB = nil) then DB:=Session.OpenDatabase('DBDEMOS');

If Assigned(DB) and DB.Active then

 Begin

  DB.StartTransaction;

  ...

 End;

<b>4.2 檢索有關BDE會話期的資訊</b>

  TSession提供了許多方法用于檢索有關BDE會話期的資訊如别名的參數等,下面簡單介紹這些方法。

.GetAliasDriverName傳回一個别名的驅動程式;

.GetAliasNames傳回所有BDE别名的清單;

.GetAliasParams傳回一個别名的參數的清單;

.GetConfigParams傳回配置檔案中的特定資訊;

.GetDatabaseNames傳回所有BDE别名的清單(含占用的别名);

.GetDriverNames傳回已安裝的驅動程式的清單;

.GetDriverParams傳回一個驅動程式的參數;

.GetStoredProcNames傳回一個資料庫中的存儲過程名;

.GetTableNames傳回一個資料庫中的表格名。

  上述方法中,除了GetAliasDriverName傳回一個字元串外,其他方法都是傳回一個清單。下面的例子試圖檢索所有的BDE别名:

  var List: TStringList;

List := TStringList.Create;

Session.GetDatabaseNames(List);

List.Free;

<b>4.3 管理BDE别名</b>

  對于BDE會話期對象來說,BDE别名特别重要,許多方法都需要傳遞一個資料庫的别名作為參數。TSession提供了管理BDE别名的功能。

<b>4.3.1 指定别名的可見性</b>

  ConfigMode屬性用于設定哪些BDE别名對BDE會話期是可見的。ConfigMode屬性是一個集合,預設值是[cmAll],相當于[cfmVirtual,cfmPersistent],表示所有的别名對BDE會話期都是可見的,包括BDE配置檔案中定義的别名、BDE會話期建立的專用别名。

  使用ConfigMode屬性的主要目的是限制别名的可見性。例如,可以把ConfigMode屬性設為[cfmVirtual],表示隻能看到本BDE會話期建立的别名,不能看到其他BDE會話期建立的别名,也不能看到BDE配置檔案中定義的永久别名。

  在BDE會話期建立的别名隻存在于記憶體中,預設情況下,隻對本BDE會話期是可見的,其他BDE會話期即使是在同一個應用程式内都無法看到這些别名。

  如果要使BDE會話期建立的别名能夠被所有的BDE會話期甚至其他應用程式看到,首先要調用SaveConfigFile把别名儲存到BDE配置檔案中,這樣,其他BDE會話期或其他應用程式才可以使用這個别名。當然,ConfigMode 屬性需要設為[cmAll]。

<b>4.3.2 建立、修改和删除别名</b>

  要建立一個SQL伺服器的别名,可以調用AddAlias函數。要建立一個本地資料庫如Paradox、dBASE或ASCII文本的别名,可以調用AddStandardAlias函數。

  AddAlias需要傳遞三個參數,其中,Name參數用于指定名稱,Driver參數用于指定SQL Links驅動程式,List參數用于指定連接配接參數。程式示例如下:

AliasParams: TStringList;

AliasParams := TStringList.Create;

With AliasParams Do

Add('OPEN MODE=READ');

Add('USER NAME=TOMSTOPPARD');

Add('SERVER NAME=ANIMALS:/CATS/PEDIGREE.GDB');

Session.AddAlias('CATS', 'INTRBASE', AliasParams);

AliasParams.Free;

  與AddAlias不同的是,AddStandardAlias用于為Paradox、dBASE或文本建立别名,不需要連接配接參數,隻需要指定一個路徑和預設的驅動程式。程式示例如下:

  AddStandardAlias('MYDBDEMOS', 'C:/TESTING/DEMOS/', 'Paradox');

  要說明的是,調用AddAlias或AddStandardAlias函數建立的别名隻存在于記憶體中,要把别名永久地儲存到BDE配置檔案中,請調用SaveConfigFile函數。

  建立了一個别名後,就可以調用ModifyAlias來修改别名的參數。ModifyAlias需要傳遞兩個參數,一個是要修改的别名,還有一個是要修改的參數的清單。

  下面這個例子把CATS别名的OPEN MODE參數設為READ/WRITE:

List: TStringList;

With List Do

Clear;

Add('OPEN MODE=READ/WRITE');

Session.ModifyAlias('CATS', List);List.Free;

  雖然CATS别名的參數有幾個,但傳遞給ModifyAlias的清單中隻需包含要修改的參數。

  要删除一個BDE會話期建立的别名,可以調用DeleteAlias函數。DeleteAlias隻需要傳遞一個參數,即要删除的别名。

  注意:調用DeleteAlias函數僅僅是從記憶體中把一個别名删掉,如果要删除的别名已經永久地儲存到BDE配置檔案中,調用了DeleteAlias函數後還需要調用SaveConfigFile函數。

<b>4.4 周遊所有的TDatabase構件</b>

  這一節要介紹TSession的兩個屬性:Databases和DatabaseCount,用這兩個屬性可以周遊由一個BDE會話期對象管理的所有的TDatabase構件。

  Databases屬性是一個數組,它的每一個元素是一個處于活動狀态的TDatabase構件,這些TDatabase構件都處于BDE會話期對象的管理之下。

  DatabaseCount屬性是一個整數,表明Databases數組中元素的個數。随着資料庫的打開和關閉,DatabaseCount屬性會自動變化。例如,在KeepConnections屬性設為False并且沒有顯式使用TDatabase構件的情況下,每打開一個資料庫,DatabaseCount屬性就會加1,每關閉一個資料庫,DatabaseCount屬性就會減1。

  DatabaseCount屬性一般要與Databases屬性配合使用。例如,下面的代碼把所有TDatabase構件的KeepConnection屬性設為True:

MaxDbCount: Integer;

With Session Do

If (DatabaseCount &gt; 0) then

For MaxDbCount := 0 to (DatabaseCount - 1) Do

Databases[MaxDbCount].KeepConnections:= True;

<b>4.5 通路Paradox表</b>

  TSession的NetFileDir屬性和PrivateDir屬性隻适用于Paradox表。其中,NetFileDir屬性用于指定Paradox網絡控制檔案即PDOXUSRS.NET所在的目錄,凡是需要共享Paradox表的應用程式必須指定這個檔案所在的目錄。PrivateDir屬性用于指定Paradox表的私有目錄,一些臨時檔案就存放在私有目錄中。

  Delphi 4會自動從BDE配置檔案中檢索網絡控制檔案的位置,并把它賦給NetFileDir屬性。也可以設定這個屬性,指定另一個合法的網絡路徑。程式示例如下:

  Session.NetFileDir := ExtractFilePath(Application.EXEName);

  注意:隻能在目前還沒有打開任何Paradox表的情況下修改NetFileDir屬性。

  如果PrivateDir屬性為空,BDE會自動把目前目錄作為私有目錄。如果要運作的應用程式在一個遠端的檔案伺服器上,為了避免老是讀寫檔案伺服器進而影響速度,最好把PrivateDir屬性設為本地的某一個驅動器。

  注意:不能在設計期設定PrivateDir屬性,否則,會出現“Directory Busy”的錯誤。另外,不要把PrivateDir屬性設為一個驅動器的根目錄,最好是子目錄。程式示例如下:

  Session.PrivateDir := 'C:/TEMP';

<b>4.6 口 令</b>

  有的Paradox表和dBASE表是被密碼保護的,通路這些表時需要提供密碼。TSession提供了若幹個方法和一個事件用于管理密碼。

<b>4.6.1 AddPassword</b>

  TSession的AddPassword函數一般在通路需要輸入密碼的Paradox或dBase表之前調用,用于提供密碼。AddPassword唯一的參數就是密碼。程式示例如下:

Passwrd: String;

Passwrd := InputBox('Enter password', 'Password:', '');

Session.AddPassword(Passwrd);

 Table1.Open

Except

  ShowMessage('Could not open table!');

Application.Terminate;

  上面這個例子中調用InputBox函數讓使用者輸入密碼,也可以調用PasswordDialog函數,或者用TEdit構件做一個編輯框,把PasswordChar屬性設為星号。

  如果用PasswordDialog函數的話,需要傳遞BDE會話期對象作為參數,程式示例如下:

Procedure TForm1.Button1Click(Sender: TObject);

If PasswordDialog(Session) then

Else

 ShowMessage('No password given, could not open table!');

  上述程式将打開一個“Enter password”對話框,如圖4.1所示。

  圖4.1 輸入密碼

  對話框上的“Add”按鈕相當于調用AddPassword函數,“Remove”按鈕相當于調用RemovePassword函數,“Remove All”按鈕相當于RemoveAllPasswords函數。

  注意:要在程式中調用PasswordDialog函數,必須引用DBPWDlg單元。

如果您沒有調用AddPassword或PasswordDialog函數來提供密碼,當通路有密碼保護的Paradox表和dBase表時,就會自動彈出如圖4.1所示的對話框,讓使用者輸入密碼。

4.6.2 RemovePassword和RemoveAllPasswords

  TSession的RemovePassword用于删除一個先前用AddPassword輸入的密碼。RemovePassword隻需要傳遞一個參數,即要删除的密碼。程式示例如下:

  Session.RemovePassword('1234');

  TSession的RemoveAllPasswords函數用于删除先前所有輸入的密碼,程式示例如下:

  Session.RemoveAllPasswords;

<b>4.6.3 OnPassword和GetPassword</b>

  當程式試圖打開一個受密碼保護的Paradox表時将觸發該事件,應當在處理這個事件的句柄中調用AddPassWord函數輸入一個密碼,然後把Continue參數設為True。

  調用GetPassword函數也會觸發OnPassword事件。下面這個例子動态地把一個方法作為處理OnPassword事件的句柄:

Procedure TForm1. FormCreate(Sender: TObject);

Session.OnPassword := Password;

  Password又調用InputBox函數打開一個輸入框讓使用者輸入密碼,如果使用者輸入了密碼的話,就把Continue參數設為True。

Procedure TForm1.Password(Sender: TObject;

var Continue: Boolean);

var Passwrd: String;

Continue := (Passwrd &gt; '');

  如果使用者輸入的密碼是錯誤的,則仍然不能打開Paradox表,是以,凡是要打開一個Paradox表的代碼必須能處理異常。

  Procedure TForm1.OpenTableBtnClick(Sender: TObject);

const CRLF = #13 + #10;

Table1.Open; {将觸發OnPassword事件}

On E:Exception Do

ShowMessage('Error!'+CRLF+E.Message+CRLF);

<b>4.7 管理多個BDE會話期對象</b>

  如果要建立一個多線程的資料庫應用程式,就需要用多個TSession構件,而且必須在設計期顯式地加到窗體或資料子產品上,還要保證它們的SessionName屬性是相異的。

  Delphi 4用TSessionList來管理和操縱一個應用程式中所有的BDE會話期對象,并且已自動聲明了TSessionList的對象示例Sessions。

  如果要動态地建立一個新的BDE會話期對象,這就要用到TSessionList的OpenSession函數。這個函數隻需要傳遞一個參數,即要建立的BDE會話期的名稱。程式示例如下:

  Sessions.OpenSession('RunTimeSession' + IntToStr(Sessions.Count + 1));

  上述代碼能保證BDE會話期的名稱不會與已有的BDE會話期重複。

  TSessionList定義了一些屬性和方法用來操縱BDE會話期對象,這裡簡單介紹一下:

.Count 傳回BDE會話期對象的個數,包括活動的和非活動的;

.FindSession 查找一個指定的BDE會話期對象,如果沒有找到,就傳回NIL;

.GetSessionNames 傳回所有BDE會話期對象的SessionName屬性組成的清單;

.List 通過這個屬性可以按名稱通路一個BDE會話期對象;

.OpenSession 動态地建立一個BDE會話期對象;

.Sessions 通過這個屬性可以按序号通路一個BDE會話期對象。

  在多線程的資料庫應用程式中,在打開一個資料庫之前,首先要檢查這個資料庫是否已經被其他線程打開。怎麼檢查呢?用TSessionList的Count屬性和Sessions屬性周遊所有的BDE會話期對象,逐個檢查每個BDE會話期對象的Databases屬性中是否包含要打開的資料庫,如果有的話,說明這個資料庫已經被某個線程打開,也就是說,不能再在這個BDE會話期内打開資料庫,您得換下一個再進行檢查。

  如果所有的BDE會話期對象都在使用這個資料庫,就必須建立一個新的BDE會話期對象,然後再打開資料庫。