1.1 关于Delphi的BDE
Delphi操作数据库主要是利用BDE来进行。BDE是基于32位Windows内核的数据库引擎和连接工具,支持现有的大多数数据库。它具有如下一些特点:
1. 为多种数据库格式提供统一的应用程序接口,包括任何ODBC数据源。
2. 适应C/S数据库应用的开发,程序设计人员可以访问所有本地和服务端的数据,并且很容易实现应用的向上兼容性。
3. 对于Delphi标准的数据库访问(如Paradox和dBASE),BDE提供的连接方式是最快的。
4. 直接实时的访问数据源。
1.1.1 如何自定义BDE的驱动程序
BDE中按照各个文件的作用可分为以下几部分,这些文件均保存在“…/Program Files/Common Files/Borland Shared/BDE”目录下。
1. 必备文件
Blw32.dll:语言驱动函数库。
Idapi32.dll:BDE基本函数库。
Fareast.bll、Usa.bll:远东语言及美国语言驱动程序,对中文软件不可缺少。
Idr20009.dll:错误信息库。
2. 数据库驱动程序(可选)
Idasci32.dll:Ascii文本数据库驱动程序函数库;
Iddao32.dll:Access数据库驱动程序函数库;
Iddbas32.dll:dBase数据库驱动程序函数库;
Idodbc32.dll:ODBC数据库驱动程序函数库;
Idpdx32.dll:Paradox数据库驱动程序函数库。
3. 其他驱动程序和配置文件(可选)
Idbat32.dll:批操作驱动程序函数库,如果不用TBatchMove组件或DbiBatchMove类函数,可以不要。
Iddr32.dll:Data Repository驱动程序函数库,如果不用Data Repository功能,可以不要。
Idprov32.dll:BDE DataSet provide驱动程序函数库,如果不用TProvider组件,可以不要。
Idqbe32.dll:QBE驱动程序函数库,如果不用Query By Example,可以不要。
Idsql32.dll:SQL查询驱动程序函数库,如果不用TQuery进行查询,可以不要。
Idapi32.cfg:BDE配置文件,如果程序中没有特殊要求,可以不要。也可以在BDE管理器中设置正确后再分发。
4. 其他文件(一般不用)
BDE32.HLP、BDE32.CNT:BDE帮助文件。
BdeAdmin.exe、BdeAdmin.HLP、BdeAdmin.CNT:BDE管理器及帮助文件。
*.BLL:其他国家和地区的语言驱动程序。
DataBump.EXE、DataBump.HLP:数据库数据转移工具及帮助文件。
Localsql.HLP、Localsql.CNT:SQL查询语句帮助文件。
Sqllnk32.HLP、Sqllnk32.CNT:SQL连接帮助文件。
1.1.2 如何注册BDE
安装BDE仅仅复制前面提到的各种文件是不行的,还要等到向注册表注册之后才可以使用。
必需的注册表项目包括:
1. BDE动态链接库文件位置设置(如图1所示)
Key:HKEY_LOCAL_MACHINE/Software/Borland/Database Engine
Item:DLLPATH
Value:BDE动态链接库文件所在位置
图1 BDE动态链接库文件位置设置
2. BDE语言驱动文件路径设置(如图2所示)
Key:HKEY_LOCAL_MACHINE/Software/Borland/BLW32
Item:BLAPIPATH
Value:BDE语言驱动文件所在路径
图2 BDE语言驱动文件路径设置
3. 定义可用的BDE语言驱动文件(如图3所示)
Key:HKEY_LOCAL_MACHINE/Software/Borland/BLW32
Item:LOCALE_LIB#" (#表示数字, 如"LOCALE_LIB1"、"LOCALE_LIB3"等)
Value:指定各BDE语言驱动文件
图3定义可用的BDE语言驱动文件
1.1.3 如何在运行期检测和建立数据库别名
在前面学习的案例中,数据库别名的建立、修改和删除等维护工作,一般在BDE Administrator中进行,并在设计期就已经设置完毕,但这些功能在运行期将如何实现呢?在Delphi的BDE中,当数据库类型为STANDARD时,其别名定义最为简单,但这时仅能使用Paradox,dBASE,ASCIIDRV三种数据库作为默认的驱动程序。另外还需定义数据库存储路径(PATH)和ENABLE BCD,这样才能建立一个完整的数据库别名。
Delphi的数据库应用程序能自动提供一个Session组件,这个Session组件即为应用程序与BDE的接口。
1. 检测别名
通过调用Session.GetAliasNames(list:Tstrings)方法,可将当前BDE配置中的所有数据库别名的名称存放到List字符串列表中。然后调用list.IndexOf(需要检测的别名\')函数,该函数的返回值可用来检测数据库别名是否存在(若其值为-1则不存在)。
2. 添加别名
使用Session组件的过程AddStandardAlias(constName,Path,DefaultDriver:string),可以添加一个标准类型的数据库别名。如添加一个数据库别名为NewAlias,其默认数据库驱动程序为Paradox、存储路径为c:/instance1,则可以使用下面的语句。
Session.AddStandardAlias(\'NewAlias\',\' c:/instance1\',\'Paradox\');
3. BDE配置文件存盘
使用Session.SaveConfigFile语句可将设置信息保存。
一般可以在窗体的OnCreate事件中添加检测和创建数据库别名的程序代码,下面就是一个在运行期实现上述功能的例子。首先单击Delphi工具栏上的“New Form”按钮,新建一窗体,在该窗体上添加一个Table组件,用以提供TSession对象。然后在窗体的OnCreate事件中添加如下所示的代码,程序执行时,系统会首先检测NewAlias别名是否存在,若不存在,提示用户是否创建这个别名,如图4所示。
Procedure TForm1.FormCreate(Sender: TObject);
var
sl:TStringList; //字符串列表变量
re:Integer;
begin
sl:=TStringlist.Create;
Session.GetAliasNames(sl); //取得别名列表
if (sl.IndexOf(\'NewAlias\')=-1) then //判断别名是否存在
begin
re:=Application.MessageBox(\'别名NewAlias不存在,现在创建吗?\',\'BDE信息窗口\',mb_OKCancel);
if re=IDCANCEL then begin
sl.Free;
Exit;
end;
Session.AddStandardAlias(\'newAlias\',\'c:/instance1\',\'Paradox\');
//增加一个名为NewAlias的数据库别名
Session.SaveConfigFile; //保存BDE配置文件
end ;
sl.free;
end;
图4 提示信息
1.1.4 Delphi连接数据库的方式有哪些
---- Delphi访问数据库的方式主要有3种,包括直接访问,如访问标准数据库类型Paradox和dBASE;通过ODBC访问,如访问Access、Foxpro等数据库;通过内嵌(Native)方式访问数据库,如访问SQLServer、Oracle、DB2等。下面以Table组件为例说明这3种方式的特点和用法。
1. 直接访问
----Delphi可以直接访问诸如Paradox和dBASE的标准数据库类型,而不需要什么特别的设置,只需要把文件路径或数据库别名赋值给Table组件的属性DatabaseName就可以访问该路径下的数据表了。
2. 通过ODBC访问
----访问Paradox和dBASED以外的数据库,通常可通过ODBC来实现。Delphi可以访问支持ODBC的数据库系统,如Access、SQLServer和Oracle等。当然,通过ODBC访问数据库时,首先要使用Windows的控制面板或Delphi的数据库引擎建立ODBC数据源。
3. 通过内嵌方式访问
Delphi中还可以不使用ODBC,而以内嵌方式访问SQLServer、Oracle、DB2等数据库系统。这需要使用数据库别名来指定数据库,数据库别名可以事先建立,也可以在程序运行时动态创建。前者称为静态别名,后者称为动态别名。使用数据库别名来访问数据库的方法和使用ODBC数据源的情形相同,这里不再赘述。
通过内嵌方式访问数据库的静态别名必须在BDE中建立。以访问SQLServer数据库为例,在建立别名时必须指定数据库服务器的名称(Servername)、主机名(Hostname)以及要访问的数据库名称(Databasename),可以指定登录用户名(Username)和密码(Password)等。
图5 设置Database组件
通过动态创建的别名来访问数据库必须使用Database组件。用户可以用鼠标双击Database组件,出现参数设置窗口,如图5所示。在“Drivername”一栏选择要访问的数据库系统,如“MSSQL”,然后选择按钮“Defaults”,就会把BDE中该数据库系统所需的参数名称和缺省值加入到“Parameteroverrides”列表中。根据实际情况更改参数中的“Servername”、“Databasename”等项。最后单击OK按钮完成设置。
比较可知,通过内嵌方式访问数据库要比通过ODBC访问数据库速度快一些。而且,内嵌方式可以在程序中动态设置连接数据库所需的参数,用户可不必设置ODBC数据源,从而降低了对用户技术水平的要求,并且减少了用户的工作量。因此从系统配置的难易和复杂程度来看,使用内嵌方式开发出的数据库应用系统更便于普通用户使用。
1.1.5 如何获取BDE的版本信息
单击Delphi工具栏中的“New Form”按钮,新建一窗体。向该窗体中添加1个Memo组件,并设置其Align属性为alclient。在窗体对应的单元文件的USES部分添加dbierrs, DBTables两个引用库文件。在下面的单元文件中,fDbiGetSysVersion函数将返回一个称为SysVersion的结构,并将最后结果显示在窗体的Memo中。
/*该窗体的单元文件代码*/
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,dbierrs, DBTables, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function fDbiGetSysVersion(SysVerList: TStringList): SYSVersion;
var
Month, Day, iHour, iMin, iSec: Word;
Year: SmallInt;
begin
Check(DbiGetSysVersion(Result));
if (SysVerList <> nil) then
begin
with SysVerList do
begin
Clear;
Add(Format(\'数据引擎版本号=%d\', [Result.iVersion]));
Add(Format(\'接口级=%d\', [Result.iIntfLevel]));
Check(DbiDateDecode(Result.dateVer, Month, Day, Year));
Add(Format(\'版本日期=%s\', [DateToStr(EncodeDate(Year, Month, Day))]));
Check(DbiTimeDecode(Result.timeVer, iHour, iMin, iSec));
Add(Format(\'版本时间=%s\', [TimeToStr(EncodeTime(iHour, iMin, iSec div 1000, iSec div 100))]));
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var hStrList: TStringList;
Ver: SYSVersion;
begin
hStrList:= TStringList.Create;
try Ver := fDbiGetSysVersion(hStrList);
except
ShowMessage(\'BDE not installed !\');
end;
Memo1.Lines.Assign(hStrList);
hStrList.Destroy;
end;
end.
按F9键运行程序,将得到如图6所示的BDE版本信息。
图6 运行结果窗口
1.1.6 如何维护DBF数据库
事实上,由于Delphi具有快捷易用、功能强大等优点,使得很多FoxPro和VFP的程序设计人员加入到了Delphi阵营。但在FoxPro和VFP的开发环境下,有许多DBF类型的数据表文件需要移植到Delphi中来,因此在Delphi中如何维护和操作这些数据将是一个很重要的问题。下面阐述在Delphi中完成DBF数据库的如下操作:真正删除记录、显示被删除记录、获取当前记录号和恢复被删除记录.
1. 真正删除记录
在Delphi程序中,使用Table或Query组件的Delete方法执行删除记录操作时,系统执行的是软删除,即相当于Foxpro中的“Set Delete Off”的效果。该方法只是将记录用星号标记为删除,实际并没有进行物理上的删除。要想真正删除记录,需要调用BDE函数DbiPackTable,该函数语法为:
functionDbiPackTable(hDb:hDBIDb;hCursor:hDBICur;pszTableName:PChar;pszDriverType:PChar;bRegenIdxs:Bool):DBIResult;
其中hDb为数据库Databse的句柄,hCursor为数据表Table的句柄,pszTableName为要删除记录的数据表名,pszDriverType为要删除记录的数据表的类型,bRegenIdxs表示是否在删除记录后自动更新索引文件。在四个参数中,hDb不能为NULL。hCursor、pszTableName、pszDriverType可以为NULL,但必须提供足够的信息来标识数据表的文件名和类型,当hCursor不为空时,pszTableName和pszDriverType可以为NULL;当pszTableName为数据表的路径和文件名时,hCursor和pszDriverType可以为NULL。
值得注意的是,在删除记录时,如果用Table来实现,则Table必须以Exclusive=True的方式打开。
2. 显示或不显示软删除记录
当DBF数据库中的记录执行软删除后,默认情况下在DBGrid等数据库显示组件中是看不见这些记录的。可以用BDE函数来控制是否显示DBF数据库中被软删除的记录,如同在Foxpro中利用语句Set Delete ON/OFF开关一样。执行这一功能的函数为DbiSetProp,其语法为:
functionDbiSetProp(hObj:hDBIObj;iProp:Longint;iPropValue:Longint):DBIResult;
该函数用来设置DBI对象中某个属性的值。其中Obj为DBI对象名称,这里为数据表Table的句柄,iProp为属性名,可使用软删除属性curSOFTDELETEON,iPropValue为属性值。
3. 获取当前记录号
在Delphi中可以使用BDE函数获取当前记录在数据集中的记录号.该函数为DbiGetRecord,其语法为:
functionDbiGetRecord(hCursor:hDBICur;eLock:DBILockType;pRecBuff:Pointer;precProps:pRECProps):DBIResult;
该函数用来取得当前记录的一些属性。其中,hCursor为数据集的Handle,eLock为对记录加锁的类型,pRecBuff存放记录的缓冲区,precProps为记录属性集,这里面就包含当前记录的记录号。
4. 恢复软删除记录
在Delphi应用程序中,对DBF数据表执行的删除操作为软删除操作。由于物理记录并没有从数据表中删除,因此可以恢复被软删除的记录,只要去掉删除标志即可。完成这一功能需使用函数DbiUndeleteRecord,其语法为:
functionDbiUndeleteRecord(hCursor:hDBICur):DBIResult;
其中,hCursor可以是数据集的Handle。
1.1.7 如何备份数据表
对于数据库应用来说,数据表的备份应该算是一个常用的功能。下面举一例,介绍其实现过程。单击Delphi工具栏上的“New Form”按钮,新建一窗体,在该窗体上添加1个Table组件、1个DataSource组件、1个DBGrid组件和2个Button组件,设置数据库之间的连接关系,并置Table的Active属性为True。窗体的布局设计如图7所示。
图7 窗体布局示例
然后在“备份表”按钮的OnClick事件中添加代码:
Procedure TForm1.Button1Click(Sender: TObject);
begin
QuickCopyTable(table1,\'c:/a.dbf\',true);
ShowMessage(\'数据表备份已完成\')
end;
其中所引用的QuickCopyTable方法的代码如下所示。
Procedure QuickCopyTable(T: TTable; DestTblName: string; Overwrite: Boolean);
var
DBType: DBINAME;
WasOpen: Boolean;
NumCopied: Word;
begin
WasOpen := T.Active; //保存表的Active属性值
if not WasOpen then T.Open; //检查表是否打开
Check(DbiGetProp(hDBIObj(T.Handle),drvDRIVERTYPE,@DBType, SizeOf(DBINAME), NumCopied)); //获得驱动程序类型字符串
Check(DBICopyTable(T.DBHandle, Overwrite, PChar(T.Tablename),
DBType, PChar(DestTblName))); //复制表
T.Active := WasOpen; //恢复表的Active属性值
end;
最后,要在单元文件的Uses备份添加dbierrs库文件。此时单击Delphi工具栏上的“Run”按钮或按下F9键,运行程序,单击“备份表”按钮,如图8所示。所备份的数据表a.dbf内容,如图9所示。
图8 运行结果窗口
图9 备份的a.dbf文件的内容
1.1.8 如何使用过滤器
在操作数据库时,经常需要对数据进行筛选过滤。例如在第四章学生学籍管理系统的案例中,有一个名为jiben.dbf的数据表,它具有XH、XM、CSRQ等多个字段,如果只想查看班级编号为001的学生记录,就需要对数据表中的信息进行过滤。下面是常用的对数据表信息进行过滤的方法:
1. 利用TTable和TQuery的Filter属性
n 在设计时设置Filter属性
例如设置Filter为(bjbh=’001’),然后改变Filtered属性为True。此时将只能看到对应的bjbh(班级编号)字段内容为’001’的记录。另外设置Filter时可以使用的操作符有:<、>、<=、>=、=、<>、AND、OR、NOT。
n 在程序运行期间进行动态过滤
要在程序运行时改变Filter属性,包括两种情况。一是操作符右边为常量,例如语句(table1.Filter:=\'bjbh\'+\'=\'+\'001\';);二是操作符右边不为常量,可能是通过一个变量指定的值,或由一输入框给出的值。此时需要使用Format函数。其代码形式为(Table1.Filter:=Format(\'bjbh\'+\'=\'+\'%S\',[bjbhvalue]);)其中bjbhvalue为已经赋值的一个字符串变量,也可以为其他形式如Edit1.Text。
2. 用ApplyRange筛选数据集的记录
Delphi的DBDEMOS数据库中有一个Customer.db数据表,如果我们想查看顾客号从1000到3000之间的顾客记录。可使用ApplyRange, SetRangeStart, SetRangeEnd过程来完成,其代码为:
Table1.SetRangeStart;
Table1[‘CustNo’]:=1000;
Table1.SetRangeEnd;
Table1[‘CustNo’]:=3000;
Table1.ApplyRange;
需要说明的是该过程只适用于索引的字段。
3. 用OnFilterRecord事件过滤
例如下面的代码:
Procedure TForm1.Table1FilterRecord(DataSet:TDataSet;varAccept:Boolean);
begin
Accept:=DataSet[‘bjbh’]=’001’;
end;
4. 用Query组件的SQL语句
n SQL语句中不包含变量和参数
Select * from Customer Where CustNo>=1000 and CustNo<=3000
n SQL语句中包含参数
Select * from Customer Where CustNo>=:p1
可在程序中给参数p1赋值,读者可参阅前面讲述的案例程序。
n SQL语句中包含变量
此时向Query组件添加SQL语句的代码如下所示:
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add(Format(‘Select * from Customer’+’’+’where CustNo=’+’%S’, [CustNoValue]));
Query1.Open;
1.2 关于字符串操作
在程序开发过程中,经常要涉及对字符串的操作。Delphi提供了一个TStrings类,用来完成存储和操作字符串,它的主要行为有:
1. 在列表中添加或删除字符串。
2. 重新排列字符串。
3. 在指定的位置存取字符串。
4. 从文件或流中读取及写入字符串
5. 为列表中的每一个字符串联系一个对象。
TStrings类的继承关系为:TObject-TPersistent
1.2.1 常用的字符串处理函数有哪些
表1列出了常用的字符串处理函数。
表1 常用字符串处理函数
函数名
|
语法
|
功能
|
AnsiCompareStr
|
function AnsiCompareStr(const S1, S2: string): Integer;
|
用于比较两个大小写敏感的字符串
|
AnsiCompareText
|
function AnsiCompareText(const S1, S2: string): Integer;
|
用于比较两个大小写不敏感的字符串
|
AnsiUpperCase
|
function AnsiUpperCase(const S: string): string;
|
将字符串转换为全部大写
|
AnsiLowerCase
|
function AnsiLowerCase(const S: string): string;
|
将字符串转换为全部小写
|
Appendstr
|
procedure AppendStr(var Dest: string; const S: string); deprecated;
|
将给定字符串常量添加到目标字符串末尾
|
CompareStr
|
function CompareStr(const S1, S2: string): Integer;
|
用于比较两个大小写敏感的字符串,其结果与区域设置无关
|
CompareText
|
function CompareText(const S1, S2: string): Integer;
|
用于比较两个大小写不敏感的字符串,其结果与区域设置无关
|
Concat
|
function Concat(s1 [, s2,..., sn]: string): string;
|
将一组字符串连接起来
|
Copy
|
function Copy(S; Index, Count: Integer): string;
|
返回字符串的子串
|
Delete
|
procedure Delete(var S: string; Index, Count:Integer);
|
从字符串中删除一个子串
|
Insert
|
procedure Insert(Source: string; var S: string; Index: Integer);
|
在字符串的指定位置插入一个子串
|
Length
|
function Length(S): Integer;
|
返回字符串中中字符的个数
|
Pos
|
function Pos(Substr: string; S: string): Integer;
|
在字符串中搜索子串,返回的是索引值
|
LeftStr
|
function LeftStr(const AText: AnsiString; const ACount: Integer): AnsiString;
|
返回从字符串左边开始指定长度的子串
|
RightStr
|
function RightStr(const AText: AnsiString; const ACount: Integer): AnsiString;
|
返回从字符串末尾向前指定长度的子串
|
InttoStr
|
function IntToStr(Value: Integer): string;
|
将整数转换为字符串
|
StrtoInt
|
function StrToInt(const S: string): Integer;
|
将字符串转换为整数
|
LowerCase
|
function LowerCase(const S: string): string;
|
转换为小写
|
UpperCase
|
function UpperCase(const S: string): string;
|
转化为大写
|
Val
|
procedure Val(S; var V; var Code: Integer);
|
将字符串的值转换为其数字表示式
|
1.2.2 TStrings对象的属性和方法有哪些
TStrings的一些重要属性和方法如下所示。
Count:该属性定义列表中字符串的数量。
Strings:该属性表示由参数Index指定位置的字符串。0表示第一个字符串,1表示第二个字符串,依此类推。
Text:TStings对象的文本,它包含一些由回车和换行符分开的字符串。
Add方法:该方法在字符串列表的末尾添加一字符串。在调用Add方法加入字符串之后,再返回新字符串的索引值。
AddObject方法:该方法向字符串列表中加入一个字符串及与它相联系的对象。调用AddObject方法之后将返回新字符串和对象的索引值。
Append方法:该方法将在字符串列表中添加一字符串,它与Add方法一样,但不返回值。
Clear方法:该方法将清空字符串列表。
Delete方法:删除指定的字符串。
Destroy方法:析构函数,毁坏一个TStrings对象实例。
IndexOf方法:function IndexOf(const S:string):integer; 该方法的功能是返回字符串S在字符串列表中的索引值。调用IndexOf函数返回的是第一次发现字符串S的位置。其返回值也以0作为起点,若返回0则表示是第一个字符串,返回1表示是第二个字符串,依此类推。如果指定的字符串S不在列表中,则返回-1。需要指出的是,若S在字符串列表中出现的次数大于1,则IndexOf方法返回的是第一次发现的位置值。下面介绍一个IndexOf方法的应用实例。
单击Delphi工具栏上的“New Form”按钮,新建一窗体,在该窗体上添加1个FileListBox组件、1个DirectoryListBox组件和2个Label组件,完成各个组件的属性设置及布局之后,在DirectoryListBox组件的OnChange事件中添加如下所示的代码。然后按F9键执行程序,其结果如图10所示。
图10 执行结果窗口
Procedure TForm1.DirectoryListBox1Change(Sender: TObject);
begin
filelistbox1.Directory:=directorylistbox1.Directory;
if filelistbox1.Items.IndexOf(\'regedit.exe\')>-1 then
showmessag
请发表评论