第四章 BDE会话期
不管是单层、两层还是多层的数据库应用程序,一般都要用到BDE(BorlandDatabase Engine)。Delphi 4用TSession来管理BDE会话期对象,用TSessionList来管理和操纵一个应用程序中所有的BDE会话期对象。
一般来说,并不需要显式地把TSession构件放到窗体或数据模块上,因为数据库应用程序在每次启动时会自动创建一个默认的BDE会话期对象叫Session。但如果开发的是一个多线程的数据库应用程序,就要显式地用到TSession构件,因为几个线程有可能要同时连接数据库,应当避免每次连接数据库时都要创建应用程序的另一个实例。
4.1 TSession
TSession构件能够对一个应用程序内的一组TDatabase构件提供全局控制。当创建数据库应用程序(包括应用服务器)时,应用程序会自动创建一个默认的BDE会话期对象叫Session。在应用程序中加入新的TDatabase构件和新的数据集构件时,这些构件会自动地处于默认的BDE会话期对象即Session的控制之下。此外,BDE会话期对象还能够提供访问Paradox表的口令,指定网络控制文件所在的目录,控制数据库的连接。
除了默认的BDE会话期对象外,有些应用程序需要用到另外的TSession构件。例如,如果应用程序要并行查询同一个数据库,此时,每一个查询都必须有单独的BDE会话期。多线程的数据库应用程序也需要有多个BDE会话期。
您既可以在设计期加入TSession构件,也可以在运行期动态地创建TSession构件。
4.1.1 默认的BDE会话期对象
所有的数据库应用程序都会自动包含一个默认的BDE会话期对象叫Session,它的SessionName属性是Default。默认的BDE会话期对象能够对所有的TDatabase构件提供全局控制,不管这些TDatabase构件是在设计期显式地加到窗体或数据模块上的还是在运行期动态创建的。注意:默认的BDE会话期对象在设计期是不存在的,它只存在于运行期。
无论是在设计期还是在运行期加入TDatabase构件时,加入的TDatabase构件都会自动处于默认的BDE会话期对象管理之下。当然,如果您显式地使用了多个TSession构件,您也可以设置TDatabase构件的SessionName属性指定其中一个TSession构件。
TSession最重要的属性是KeepConnections,如果这个属性设为True,即使当前没有打开数据集,也保持与数据库的连接。这样,下次打开数据集时,就不必再登录数据库。
注意:应用程序千万不要试图删除默认的BDE会话期对象。
4.1.2 创建另外的BDE会话期对象
有些情况下需要用到另外的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;
End;
实际上,也可以调用TSessionList的OpenSession函数来创建BDE会话期对象,这个函数需要传递一个SessionName参数,指定要创建的BDE会话期对象的名称。如果与SessionName参数匹配的BDE会话期对象已存在,这个函数就激活它。程序示例如下:
Var MySession : TSession;
MySession := Sessions.OpenSession('MySession');
...
MySession.Free;
4.1.3 给BDE会话期对象命名
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;
...
Begin
IBSession := Sessions.OpenSession('InterBaseSession');
Database1.SessionName := 'InterBaseSession';
End;
4.1.4 激活BDE会话期对象
如果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别名是可见的。
4.1.5 KeepConnections属性
如果KeepConnections属性设为True,即使当前没有打开任何数据集,也保持与数据库的连接。如果这个属性设为False,当所有的数据集都关闭时,就断开与数据库的连接。
这个属性是针对动态生成的临时的TDatabase构件而言的,如果显式地使用了TDatabase构件,将以它的KeepConnections属性为准。
如果一个应用程序需要频繁地打开和关闭所有的数据集,特别是当这些数据集是在一个远程数服务器上时,最好把KeepConnections属性设为True,这样可以避免再次登录到远程服务器。否则,应用程序不得不重新登录。
不过,即使在KeepConnections属性设为True的情况下,仍然可以调用DropConnections函数把空的连接断开。所谓空的连接,是指当前并没有打开任何数据集,但由于KeepConnections属性设为True,仍然保持在连接状态。
4.1.6 打开和断开连接
要打开一个数据库的连接,调用OpenDatabase函数。这个函数需要传递一个DatabaseName参数,用于指定要打开的数据库的名称,可以设为BDE别名或TDatabase构件的名称。对于Paradox或dBASE,DatabaseName参数也可以设为表所在的路径。
下面的程序示例试图打开别名为DBDEMOS的数据库:
var
DBDemosDatabase: TDatabase;
Begin
DBDemosDatabase := Session.OpenDatabase('DBDEMOS');
...
End;
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的数据库:
var
DB: TDatabase;
Begin
DB := Session.FindDatabase('DBDEMOS');
If (DB = nil) then DB:=Session.OpenDatabase('DBDEMOS');
If Assigned(DB) and DB.Active then
Begin
DB.StartTransaction;
...
End;
End;
4.2 检索有关BDE会话期的信息
TSession提供了许多方法用于检索有关BDE会话期的信息如别名的参数等,下面简单介绍这些方法。
.GetAliasDriverName返回一个别名的驱动程序;
.GetAliasNames返回所有BDE别名的列表;
.GetAliasParams返回一个别名的参数的列表;
.GetConfigParams返回配置文件中的特定信息;
.GetDatabaseNames返回所有BDE别名的列表(含占用的别名);
.GetDriverNames返回已安装的驱动程序的列表;
.GetDriverParams返回一个驱动程序的参数;
.GetStoredProcNames返回一个数据库中的存储过程名;
.GetTableNames返回一个数据库中的表格名。
上述方法中,除了GetAliasDriverName返回一个字符串外,其他方法都是返回一个列表。下面的例子试图检索所有的BDE别名:
var List: TStringList;
Begin
List := TStringList.Create;
Try
Session.GetDatabaseNames(List);
...
Finally
List.Free;
End;
End;
4.3 管理BDE别名
对于BDE会话期对象来说,BDE别名特别重要,许多方法都需要传递一个数据库的别名作为参数。TSession提供了管理BDE别名的功能。
4.3.1 指定别名的可见性
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]。
4.3.2 创建、修改和删除别名
要创建一个SQL服务器的别名,可以调用AddAlias函数。要创建一个本地数据库如Paradox、dBASE或ASCII文本的别名,可以调用AddStandardAlias函数。
AddAlias需要传递三个参数,其中,Name参数用于指定名称,Driver参数用于指定SQL Links驱动程序,List参数用于指定连接参数。程序示例如下:
var
AliasParams: TStringList;
Begin
AliasParams := TStringList.Create;
Try
With AliasParams Do
Begin
Add('OPEN MODE=READ');
Add('USER NAME=TOMSTOPPARD');
Add('SERVER NAME=ANIMALS:/CATS/PEDIGREE.GDB');
End;
Session.AddAlias('CATS', 'INTRBASE', AliasParams);
Finally
AliasParams.Free;
End;
End;
与AddAlias不同的是,AddStandardAlias用于为Paradox、dBASE或文本创建别名,不需要连接参数,只需要指定一个路径和默认的驱动程序。程序示例如下:
AddStandardAlias('MYDBDEMOS', 'C:/TESTING/DEMOS/', 'Paradox');
要说明的是,调用AddAlias或AddStandardAlias函数创建的别名只存在于内存中,要把别名永久地保存到BDE配置文件中,请调用SaveConfigFile函数。
创建了一个别名后,就可以调用ModifyAlias来修改别名的参数。ModifyAlias需要传递两个参数,一个是要修改的别名,还有一个是要修改的参数的列表。
下面这个例子把CATS别名的OPEN MODE参数设为READ/WRITE:
var
List: TStringList;
Begin
List := TStringList.Create;
With List Do
Begin
Clear;
Add('OPEN MODE=READ/WRITE');
End;
Session.ModifyAlias('CATS', List);List.Free;
...
End;
虽然CATS别名的参数有几个,但传递给ModifyAlias的列表中只需包含要修改的参数。
要删除一个BDE会话期创建的别名,可以调用DeleteAlias函数。DeleteAlias只需要传递一个参数,即要删除的别名。
注意:调用DeleteAlias函数仅仅是从内存中把一个别名删掉,如果要删除的别名已经永久地保存到BDE配置文件中,调用了DeleteAlias函数后还需要调用SaveConfigFile函数。
4.4 遍历所有的TDatabase构件
这一节要介绍TSession的两个属性:Databases和DatabaseCount,用这两个属性可以遍历由一个BDE会话期对象管理的所有的TDatabase构件。
Databases属性是一个数组,它的每一个元素是一个处于活动状态的TDatabase构件,这些TDatabase构件都处于BDE会话期对象的管理之下。
DatabaseCount属性是一个整数,表明Databases数组中元素的个数。随着数据库的打开和关闭,DatabaseCount属性会自动变化。例如,在KeepConnections属性设为False并且没有显式使用TDatabase构件的情况下,每打开一个数据库,DatabaseCount属性就会加1,每关闭一个数据库,DatabaseCount属性就会减1。
DatabaseCount属性一般要与Databases属性配合使用。例如,下面的代码把所有TDatabase构件的KeepConnection属性设为True:
var
MaxDbCount: Integer;
Begin
With Session Do
If (DatabaseCount > 0) then
For MaxDbCount := 0 to (DatabaseCount - 1) Do
Databases[MaxDbCount].KeepConnections:= True;
End;
4.5 访问Paradox表
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';
4.6 口 令
有的Paradox表和dBASE表是被口令保护的,访问这些表时需要提供口令。TSession提供了若干个方法和一个事件用于管理口令。
4.6.1 AddPassword
TSession的AddPassword函数一般在访问需要输入口令的Paradox或dBase表之前调用,用于提供口令。AddPassword唯一的参数就是口令。程序示例如下:
var
Passwrd: String;
Begin
Passwrd := InputBox('Enter password', 'Password:', '');
Session.AddPassword(Passwrd);
Try
Table1.Open
Except
ShowMessage('Could not open table!');
Application.Terminate;
End;
End;
上面这个例子中调用InputBox函数让用户输入口令,也可以调用PasswordDialog函数,或者用TEdit构件做一个编辑框,把PasswordChar属性设为星号。
如果用PasswordDialog函数的话,需要传递BDE会话期对象作为参数,程序示例如下:
Procedure TForm1.Button1Click(Sender: TObject);
Begin
If PasswordDialog(Session) then
Table1.Open
Else
ShowMessage('No password given, could not open table!');
End;
上述程序将打开一个“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;
4.6.3 OnPassword和GetPassword
当程序试图打开一个受口令保护的Paradox表时将触发该事件,应当在处理这个事件的句柄中调用AddPassWord函数输入一个口令,然后把Continue参数设为True。
调用GetPassword函数也会触发OnPassword事件。下面这个例子动态地把一个方法作为处理OnPassword事件的句柄:
Procedure TForm1. FormCreate(Sender: TObject);
Begin
Session.OnPassword := Password;
End;
Password又调用InputBox函数打开一个输入框让用户输入口令,如果用户输入了口令的话,就把Continue参数设为True。
Procedure TForm1.Password(Sender: TObject;
var Continue: Boolean);
var Passwrd: String;
Begin
Passwrd := InputBox('Enter password', 'Password:', '');
Continue := (Passwrd > '');
Session.AddPassword(Passwrd);
End;
如果用户输入的口令是错误的,则仍然不能打开Paradox表,因此,凡是要打开一个Paradox表的代码必须能处理异常。
Procedure TForm1.OpenTableBtnClick(Sender: TObject);
const CRLF = #13 + #10;
Begin
Try
Table1.Open; {将触发OnPassword事件}
Except
On E:Exception Do
Begin
ShowMessage('Error!'+CRLF+E.Message+CRLF);
Application.Terminate;
End;
End;
End;
4.7 管理多个BDE会话期对象
如果要创建一个多线程的数据库应用程序,就需要用多个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会话期对象,然后再打开数据库。
不管是单层、两层还是多层的数据库应用程序,一般都要用到BDE(BorlandDatabase Engine)。Delphi 4用TSession来管理BDE会话期对象,用TSessionList来管理和操纵一个应用程序中所有的BDE会话期对象。
一般来说,并不需要显式地把TSession构件放到窗体或数据模块上,因为数据库应用程序在每次启动时会自动创建一个默认的BDE会话期对象叫Session。但如果开发的是一个多线程的数据库应用程序,就要显式地用到TSession构件,因为几个线程有可能要同时连接数据库,应当避免每次连接数据库时都要创建应用程序的另一个实例。
4.1 TSession
TSession构件能够对一个应用程序内的一组TDatabase构件提供全局控制。当创建数据库应用程序(包括应用服务器)时,应用程序会自动创建一个默认的BDE会话期对象叫Session。在应用程序中加入新的TDatabase构件和新的数据集构件时,这些构件会自动地处于默认的BDE会话期对象即Session的控制之下。此外,BDE会话期对象还能够提供访问Paradox表的口令,指定网络控制文件所在的目录,控制数据库的连接。
除了默认的BDE会话期对象外,有些应用程序需要用到另外的TSession构件。例如,如果应用程序要并行查询同一个数据库,此时,每一个查询都必须有单独的BDE会话期。多线程的数据库应用程序也需要有多个BDE会话期。
您既可以在设计期加入TSession构件,也可以在运行期动态地创建TSession构件。
4.1.1 默认的BDE会话期对象
所有的数据库应用程序都会自动包含一个默认的BDE会话期对象叫Session,它的SessionName属性是Default。默认的BDE会话期对象能够对所有的TDatabase构件提供全局控制,不管这些TDatabase构件是在设计期显式地加到窗体或数据模块上的还是在运行期动态创建的。注意:默认的BDE会话期对象在设计期是不存在的,它只存在于运行期。
无论是在设计期还是在运行期加入TDatabase构件时,加入的TDatabase构件都会自动处于默认的BDE会话期对象管理之下。当然,如果您显式地使用了多个TSession构件,您也可以设置TDatabase构件的SessionName属性指定其中一个TSession构件。
TSession最重要的属性是KeepConnections,如果这个属性设为True,即使当前没有打开数据集,也保持与数据库的连接。这样,下次打开数据集时,就不必再登录数据库。
注意:应用程序千万不要试图删除默认的BDE会话期对象。
4.1.2 创建另外的BDE会话期对象
有些情况下需要用到另外的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;
End;
实际上,也可以调用TSessionList的OpenSession函数来创建BDE会话期对象,这个函数需要传递一个SessionName参数,指定要创建的BDE会话期对象的名称。如果与SessionName参数匹配的BDE会话期对象已存在,这个函数就激活它。程序示例如下:
Var MySession : TSession;
MySession := Sessions.OpenSession('MySession');
...
MySession.Free;
4.1.3 给BDE会话期对象命名
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;
...
Begin
IBSession := Sessions.OpenSession('InterBaseSession');
Database1.SessionName := 'InterBaseSession';
End;
4.1.4 激活BDE会话期对象
如果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别名是可见的。
4.1.5 KeepConnections属性
如果KeepConnections属性设为True,即使当前没有打开任何数据集,也保持与数据库的连接。如果这个属性设为False,当所有的数据集都关闭时,就断开与数据库的连接。
这个属性是针对动态生成的临时的TDatabase构件而言的,如果显式地使用了TDatabase构件,将以它的KeepConnections属性为准。
如果一个应用程序需要频繁地打开和关闭所有的数据集,特别是当这些数据集是在一个远程数服务器上时,最好把KeepConnections属性设为True,这样可以避免再次登录到远程服务器。否则,应用程序不得不重新登录。
不过,即使在KeepConnections属性设为True的情况下,仍然可以调用DropConnections函数把空的连接断开。所谓空的连接,是指当前并没有打开任何数据集,但由于KeepConnections属性设为True,仍然保持在连接状态。
4.1.6 打开和断开连接
要打开一个数据库的连接,调用OpenDatabase函数。这个函数需要传递一个DatabaseName参数,用于指定要打开的数据库的名称,可以设为BDE别名或TDatabase构件的名称。对于Paradox或dBASE,DatabaseName参数也可以设为表所在的路径。
下面的程序示例试图打开别名为DBDEMOS的数据库:
var
DBDemosDatabase: TDatabase;
Begin
DBDemosDatabase := Session.OpenDatabase('DBDEMOS');
...
End;
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的数据库:
var
DB: TDatabase;
Begin
DB := Session.FindDatabase('DBDEMOS');
If (DB = nil) then DB:=Session.OpenDatabase('DBDEMOS');
If Assigned(DB) and DB.Active then
Begin
DB.StartTransaction;
...
End;
End;
4.2 检索有关BDE会话期的信息
TSession提供了许多方法用于检索有关BDE会话期的信息如别名的参数等,下面简单介绍这些方法。
.GetAliasDriverName返回一个别名的驱动程序;
.GetAliasNames返回所有BDE别名的列表;
.GetAliasParams返回一个别名的参数的列表;
.GetConfigParams返回配置文件中的特定信息;
.GetDatabaseNames返回所有BDE别名的列表(含占用的别名);
.GetDriverNames返回已安装的驱动程序的列表;
.GetDriverParams返回一个驱动程序的参数;
.GetStoredProcNames返回一个数据库中的存储过程名;
.GetTableNames返回一个数据库中的表格名。
上述方法中,除了GetAliasDriverName返回一个字符串外,其他方法都是返回一个列表。下面的例子试图检索所有的BDE别名:
var List: TStringList;
Begin
List := TStringList.Create;
Try
Session.GetDatabaseNames(List);
...
Finally
List.Free;
End;
End;
4.3 管理BDE别名
对于BDE会话期对象来说,BDE别名特别重要,许多方法都需要传递一个数据库的别名作为参数。TSession提供了管理BDE别名的功能。
4.3.1 指定别名的可见性
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]。
4.3.2 创建、修改和删除别名
要创建一个SQL服务器的别名,可以调用AddAlias函数。要创建一个本地数据库如Paradox、dBASE或ASCII文本的别名,可以调用AddStandardAlias函数。
AddAlias需要传递三个参数,其中,Name参数用于指定名称,Driver参数用于指定SQL Links驱动程序,List参数用于指定连接参数。程序示例如下:
var
AliasParams: TStringList;
Begin
AliasParams := TStringList.Create;
Try
With AliasParams Do
Begin
Add('OPEN MODE=READ');
Add('USER NAME=TOMSTOPPARD');
Add('SERVER NAME=ANIMALS:/CATS/PEDIGREE.GDB');
End;
Session.AddAlias('CATS', 'INTRBASE', AliasParams);
Finally
AliasParams.Free;
End;
End;
与AddAlias不同的是,AddStandardAlias用于为Paradox、dBASE或文本创建别名,不需要连接参数,只需要指定一个路径和默认的驱动程序。程序示例如下:
AddStandardAlias('MYDBDEMOS', 'C:/TESTING/DEMOS/', 'Paradox');
要说明的是,调用AddAlias或AddStandardAlias函数创建的别名只存在于内存中,要把别名永久地保存到BDE配置文件中,请调用SaveConfigFile函数。
创建了一个别名后,就可以调用ModifyAlias来修改别名的参数。ModifyAlias需要传递两个参数,一个是要修改的别名,还有一个是要修改的参数的列表。
下面这个例子把CATS别名的OPEN MODE参数设为READ/WRITE:
var
List: TStringList;
Begin
List := TStringList.Create;
With List Do
Begin
Clear;
Add('OPEN MODE=READ/WRITE');
End;
Session.ModifyAlias('CATS', List);List.Free;
...
End;
虽然CATS别名的参数有几个,但传递给ModifyAlias的列表中只需包含要修改的参数。
要删除一个BDE会话期创建的别名,可以调用DeleteAlias函数。DeleteAlias只需要传递一个参数,即要删除的别名。
注意:调用DeleteAlias函数仅仅是从内存中把一个别名删掉,如果要删除的别名已经永久地保存到BDE配置文件中,调用了DeleteAlias函数后还需要调用SaveConfigFile函数。
4.4 遍历所有的TDatabase构件
这一节要介绍TSession的两个属性:Databases和DatabaseCount,用这两个属性可以遍历由一个BDE会话期对象管理的所有的TDatabase构件。
Databases属性是一个数组,它的每一个元素是一个处于活动状态的TDatabase构件,这些TDatabase构件都处于BDE会话期对象的管理之下。
DatabaseCount属性是一个整数,表明Databases数组中元素的个数。随着数据库的打开和关闭,DatabaseCount属性会自动变化。例如,在KeepConnections属性设为False并且没有显式使用TDatabase构件的情况下,每打开一个数据库,DatabaseCount属性就会加1,每关闭一个数据库,DatabaseCount属性就会减1。
DatabaseCount属性一般要与Databases属性配合使用。例如,下面的代码把所有TDatabase构件的KeepConnection属性设为True:
var
MaxDbCount: Integer;
Begin
With Session Do
If (DatabaseCount > 0) then
For MaxDbCount := 0 to (DatabaseCount - 1) Do
Databases[MaxDbCount].KeepConnections:= True;
End;
4.5 访问Paradox表
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';
4.6 口 令
有的Paradox表和dBASE表是被口令保护的,访问这些表时需要提供口令。TSession提供了若干个方法和一个事件用于管理口令。
4.6.1 AddPassword
TSession的AddPassword函数一般在访问需要输入口令的Paradox或dBase表之前调用,用于提供口令。AddPassword唯一的参数就是口令。程序示例如下:
var
Passwrd: String;
Begin
Passwrd := InputBox('Enter password', 'Password:', '');
Session.AddPassword(Passwrd);
Try
Table1.Open
Except
ShowMessage('Could not open table!');
Application.Terminate;
End;
End;
上面这个例子中调用InputBox函数让用户输入口令,也可以调用PasswordDialog函数,或者用TEdit构件做一个编辑框,把PasswordChar属性设为星号。
如果用PasswordDialog函数的话,需要传递BDE会话期对象作为参数,程序示例如下:
Procedure TForm1.Button1Click(Sender: TObject);
Begin
If PasswordDialog(Session) then
Table1.Open
Else
ShowMessage('No password given, could not open table!');
End;
上述程序将打开一个“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;
4.6.3 OnPassword和GetPassword
当程序试图打开一个受口令保护的Paradox表时将触发该事件,应当在处理这个事件的句柄中调用AddPassWord函数输入一个口令,然后把Continue参数设为True。
调用GetPassword函数也会触发OnPassword事件。下面这个例子动态地把一个方法作为处理OnPassword事件的句柄:
Procedure TForm1. FormCreate(Sender: TObject);
Begin
Session.OnPassword := Password;
End;
Password又调用InputBox函数打开一个输入框让用户输入口令,如果用户输入了口令的话,就把Continue参数设为True。
Procedure TForm1.Password(Sender: TObject;
var Continue: Boolean);
var Passwrd: String;
Begin
Passwrd := InputBox('Enter password', 'Password:', '');
Continue := (Passwrd > '');
Session.AddPassword(Passwrd);
End;
如果用户输入的口令是错误的,则仍然不能打开Paradox表,因此,凡是要打开一个Paradox表的代码必须能处理异常。
Procedure TForm1.OpenTableBtnClick(Sender: TObject);
const CRLF = #13 + #10;
Begin
Try
Table1.Open; {将触发OnPassword事件}
Except
On E:Exception Do
Begin
ShowMessage('Error!'+CRLF+E.Message+CRLF);
Application.Terminate;
End;
End;
End;
4.7 管理多个BDE会话期对象
如果要创建一个多线程的数据库应用程序,就需要用多个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会话期对象,然后再打开数据库。