当前位置: 首页 > 文档资料 > Delphi 基础教程 >

第十五章 数据访问部件的应用及编程(二)

优质
小牛编辑
127浏览
2023-12-01
第十五章 数据访问部件的应用及编程(二)

15.3.6 数据集部件的事件

数据集部件TTable或TQuery具有很多的事件。为这些事件编写相应的程序代码可以进行有效性验证、计算可计算字段的值、确认对数据库表的多种操作等等。这些事件及其描述如表15.4所示。

表15.4 数据集部件常用的事件

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

事 件 描 述

───────────────────────────────────

BeforeOpen,Afteropen 在数据集部件被打开之前/之后被触发

───────────────────────────────────

BeforeClose,Afterclose 在数据集部件被关闭之前/之后被触发

───────────────────────────────────

BeforeInsert,AfterInsert 在数据集部件进入插入状态之前/之后被触发

───────────────────────────────────

BeforeEdit,AfterEdit 在数据集部件被编辑之前/之后被触发

───────────────────────────────────

BeforePost,AfterPost 在数据集部件投寄被修改的记录之前/之后被触发

───────────────────────────────────

BeforeCancel,AfterCancel 在数据集部件取消前一步操作之前/之后被触发

───────────────────────────────────

BeforeDelete,AfterDelete 在数据集部件删除当前记录之前/之后被触发

───────────────────────────────────

OnNewRecord 当建立一条新记录时被触发

───────────────────────────────────

OnCalcFields 当为表中的计算字段计算字段值时被触发

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

15.4 TTable部件及应用

在前一节里我们介绍了数据集部件TTable 和TQuery 的共同的一些属性和方法。TTable部件是Delphi数据库编程中要经常使用的最重要的部件之一,它是数据库应用程序访问数据库时必须使用的数据集部件之一,在这一节里,我们重点介绍TTable部件特有的属性和方法,TTable部件所有的属性、方法和事件都可以在联机帮助中查到。

15.4.1 TTabel部件主要的属性

DatabaseName属性和TableName属性:

DatabaseName属性是说明数据库应用程序所操作的数据库的名字,它可以是由BDE定义的数据库的别名、显式说明的数据库文件所在的磁盘路径或者由TDatabase部件定义的一个数据库名。DatabaseName属性常常是一个由BDE定义的数据库的别名。使用由BDE定义的数据库的别名代替数据库实际所在的路径和名字,好处是当实际的数据库存放的位置发生变化时,只需利用BDE简单地设置一下该数据库的别名,而数据库应用程序无需修改。有关BDE的使用请参看BDE的设置应用。TabelName属性用以说明当前TTable部件所连接的实际的数据库表。这两个属性一般都在设计阶段指定,当然在程序运行过程中也可以设置,但是要修改这两个属性时, 必须要在TTabel的Active属性为False时进行,当TTable的Active属性为True时,这两个属性是不能被修改和设置的。

TableType属性:

该属性说明与TTable部件相连接的数据库表的类型。当TableType属性设置成Default时,该属性所说明的数据库表的类型由数据库文件的扩展名决定。

若数据库文件的扩展名为.DB或没有扩展名,表的类型是Paradox表

若数据库文件的扩展名为.DBF时,表的类型是dBASE表

若数据库文件的扩展名为.TXT时,表的类型是ASCII表

如果TableType属性不设定为Default,那么与TTable 部件相连的数据库表的类型由TableType中的设置的值决定,不用考虑数据库文件的扩展名。

KeyExclusive属性:

该属性的一个作用是说明在数据库表中查找记录时,将记录移到与查找值相匹配的记录处还是将记录指针移到与查找值相匹配的记录后面一条记录处。 该属性是布尔型变量,当它的值为False时(缺省情况下为False), 将记录指针移到相匹配的记录处,为True时,将记录指针移到相匹配记录的后面一条记录处。该属性另一个作用是在表中指定检索范围时,用来说明是否包括满足过滤条件的边界记录。当KeyExclusive的值为False时,检索范围包括边界记录,否则不包括边界记录,有关详细的操作请参看“限定表中记录的检索范围”。

IndexFields属性和IndexFieldsCount属性:

IndexFields的属性值是数据库表中字段名列表,它包含与TTable部件相连的数据库表中的全部索引字希。IndexFieldsCount属性说明表中索引字段的个数。这两个属性值都是只读的,只有在程序运行过程中可用。

IndexName属性和IndexFieldNames属性:

IndexName属性中存放着在建立数据库表时为数据库表定义的所有辅助索引名,它是一个辅助索引名列表,是只读属性。IndexFieldNames属性指定用于数据库表索引排序的字段名,多个字段名之间用分号隔开。例如对Customer.DB表中的客户记录按邮政编码ZipCode和客户号码CustNo排序时可以设定IndexFieldNames的值为:

ZipCode ; CustNo

在IndexFieldNames属性中指定的字段必须存在于相应的数据库表中,否则会导致错误。IndexName和IndexFieldName是互斥的,每次只能指定其中一个属性的值,不能同时为两个属性都指定属性值。

Exclusive属性:

该属性是一个布尔型属性,它标明是否以共享方式打开数据库表,如果Exclusive的值为True,当打开一个数据库表时,其他用户就不能访问该表了,若Exclusive的值为False,将以共享方式打开一个数据库表。 显然不能将其他用户正在访问的表以互斥方式打开(设定Exclusive的值为True)。对于SQL数据库服务器上的数据库表,当以互斥方式被一个用户打开时,其他用户可以读取该表中的数据,但不能修改表中的数据,当然有些数据库服务器不支持这种方式,这要具体参看有关的数据库服务器的文档。

ReadOnly属性和CanModify属性:

这两个属性都是布尔型属性,ReadOnly属性决定用户是否能够对表中的数据进行读写。ReadOnly为True 时,用户只能读取表中的数据,ReadOnly为False时,用户可以读写表中的数据(假设数据库已授权用户能够读写其中的数据库表)。CanModify属性是一个只读属性,用户不能够修改其属性值,它反映了用户对数据库表拥有的实际特权,当ReadOnly为True时CanModify将自动地被置为False,当ReadOnly为False时,如果数据库允许用户对表进行读写时,CanModify为True,否则CanModify为False。当CanModify为False时,数据库表是只读的,但不能将其置成编辑状态或插入状态;当CanModify属性为True时,虽然数据库表对应的数据集部件可以置成编辑和插入状态,但是这并不意味着用户能够插入和修改表中的数据,因为这还要受到其他因素的限制,如用户对SQL数据库服务器的访问权限等的限制。

TTable部件还有其他一些属性请参看联机帮助

15.4.2 TTable部件的方法及应用

15.4.2.1 设定数据库表的使用范围

在我们实际应用中的数据库表中常常存放着大量的数据信息,其中包含着很多的记录,而我们的应用程序可能只需对其中一部分记录进行操作,因此,为应用程序指定一个使用范围就显得特别重要了,为方便有效地指定数据库表的使用范围Delphi为TTable部件提供了下列方法供用户使用:

SetRangeStart和EditRangeStart方法

SetRangeEnd和EditRangeEnd方法

SetRange([Start Values],[End Values])方法

ApplyRange方法

CancelRange方法

1. SetRangeStart方法

用于指定检索范围的起始记录,调用SetRangeStart方法之后,可以为起始记录的一个或多个字段指定相应的字段值。SetRangeEnd方法用于指定检索范围的结束记录,调用SetRangeEnd方法之后,可以为结束记录的一个或多个字段指定相应的字段值。

2. SetRange方法

SetRange方法包含了SetRangeStart和SetRangeEnd方法的功能,它可以同时指定检索范围的起始和结束记录,起始记录和结束记录的字段值以数组形式送给SetRange,其基本形式是:

SetRange([起始值],[结束值])

3. ApplyRange方法

根据SetRangeStart,SetRangeEnd或SetRange方法说明的检索范围的起始和结束记录,具体设定一个检索范围,调用ApplyRange方法之后, 应用程序只能对检索范围内的记录进行有关的操作。

 

4. CancelRange方法

CancelRange方法的作用与ApplyRange方法的作用是相反的,这是取消为表设定的检索范围,调用CancelRange方法之后应用程序可以对表中全部记录进行有关的操作。

在这里要注意的是:如果我们使用的是paradox表或dBASE表,在调用SetRangeStart,SetRangeEnd以及SetRange方法时,只能为表中的索引字段或定义的索引指定相应的字段值,以设定检索范围。如果使用SQL数据库服务器中的数据库表,可以为IndexFieldNames属性中指定的字段指定相应的字段值。

例如:假设Table1与Customer.DB表相连,Customer.DB中一个索引字段是CustNo,同时应用窗体中有两个编辑框StartVal和EndVal用于输入起始、结束记录的字段CustNo的值,下面的程序代码便可以为我们设定一个检索范围:

Tabel1.SetRangeStart; {指定检索范围的起始记录}

Tabel1CustNo.AsString:= StartVal.Text {为起始记录的CustNo字段指定字段值}

Tabel1.SetRangeEnd; {指定检索范围的结束记录}

if EndVal.Text <> ' ' then

Tabel1CustNo.AsString := EndVal.Text; {为结束记录的CustNo 字段指定字段值}

Tabel1.ApplyRange; {根据检索范围的起始、结束记录设定检索范围}

注意上面的程序代码,在为结束记录的CustNo字段指定字段值时, 首先检查EndVal的值是否为空,如果EndVal的值为空,那么设定的检索范围没有包含一条记录, 因为没有任何记录的字段值小于NIL;如果StartVal的值为空,那么检索范围将从表中的第一条记录开始,因为表中任何记录的字段值都大于空(NIL)。

上述代码可以用SetRange方法改写成:

If EndVal.Text <>' ' then

Tabel1.SetRane([StartVal.Text].[EndVal.Text]);

Table1.ApplyRange;

EditRangeStart和EditRangeEnd方法的使用完全类似于SetRangeStart和SetRangeEnd方法,只是调这两个方法是设定一个可编辑的范围。

又如:假设一个表中的一个索引包含两个字段LastName和FirstName,我们为索引中的一个字段或多个字段指定相应的字段值,设定数据库表的使用范围。

Table1.SetRangeStart;

Table1.FieldByName('LastName').Asstring := 'Smith';

Table1.SetRangeEnd;

Tabel1.ApplyRange;

上述代码设定的范围包括LastName字段的值大于或等于Smith的所有记录。而下面的代码设定的范围则包括LastName字段的值大于或等于Smith且FirstName字段的值大于或等于'J'的记录。

Table1.SetRangeStart;

Table1.FieldByName('LastName').Asstring := 'Smith';

Table1.FieldByName('FirstName').Asstring := 'J';

Table1.SetRangeEnd;

Tabel1.ApplyRange;

15.4.2.2 查找数据库表中的记录

如果想查找数据库表中的记录,必须想指定查找记录的一些字段的字段值,然后在表中进行检索,检索出与查找值相匹配的记录来。如果我们是在Paradox或dBASE数据库中的表中查找记录,那么查找值所对应的字段必须是表中的关键字段或辅助索引字段。如果查找SQL数据库服务器中的表,那么查找值必须是表的IndexFieldNames属性中指定的字段。

Delphi提供了两种方式在数据库表中查找记录:Goto方式和Find方式。这两种方式十分相似,它们的主要区别在于为查找指定查找值的方法不一样。

使用Goto方式进行数据查找使用的方法有SetKey方法、GotoKey方法和GotoNearest方法。其实际步骤如下:

①确保要查找的字段是关键字段或辅助索引字段。

②调用SetKey方法把与表对应的TTable部件置成查找状态。

③把查找值赋给相应的字段。

④调用GotoKey方法,并测试它的返回值检验查找是否成功。

假设Table1对应的表中第一个字段是关键字段,Edit1是应用窗体中的一个编辑框,用户可以通过Edit1输入查找值。下面的代码将通过Goto方式进行查找。

Table1.SetKey; {将Table1置成查找状态}

Table1.Field[0].AsString := Edit1.Text; {指定查找值}

Table1.GotoKey; {进行查找}

上面最后一行代码是根据用户指定的查找值,在表中执行查找。查找的结果有两种,也许成功也许失败,这是由调用GotoKey方法之后返回的布尔值来决定,如果返回True,那么查找成功,并且记录指针会指向与查找值匹配的记录,如果返回Fale,那么查找失败,记录指针的位置不发生变化。下面的代码可以测试调用GotoKey方法之后的返回值,告知用户查找是否成功。

Table1.SetKey;

Table1.Field[0].AsString:= 'Smith';

If not Table1.GotoKey then

ShowMessage('记录没找到')

在这一段代码中,如果在表中没有找到第一个字段值为Smith的记录,该应用程序会弹出一个对话框告知用户“记录没有找到”。

如果在表中存在多个关键字段或辅助索引中包含多个字段时,你在进行查找时,只想为第一个字段指定查找值,那么必须要设置TTable部件的KeyFieldCount的属性值为1。如果想为多个字段指定查找值,只能为相邻的字段指定查找值,例如辅助索引中共有三个字段,那么我们只能为第一个字段、第一和第二个字段、第一和第二以及第三个字段指定查找值,而不能为第一和第三个字段指定查找值。

GotoNearest方法的使用与GotoKey方法完全一样,只是它用于不精确查找,它不要求查找结果与查找值精确匹配,当表中有与查找值精确匹配的记录时,它将记录指针移到该记录处,当表中没有与查找值精确匹配的记录时,它会查找出与查找值最接近的记录,并将记录指针移到该记录处。

下面是应用GotoNearest方法的一段代码:

Table1.SetKey;

Table1.Fields[0].AsString:= 'Sm';

Table1.GotoNearest;

执行上述代码后,若表中存在第一个字段值等于Sm的记录时,记录指针将移到该记录处,若表中不存在第一个字段值等于Sm的记录,而存在第一个字段值等于Smith的记录,那么记录指针会移到该记录处。

如果我们不是以数据库表中的关键字段作为查找字段,我们也可以为TTable部件的IndexFieldName属性中的字段或IndexName属性中的字段指定查找值进行数据查找。例如,假设Customer表中有一个名叫CityIndex的辅助索引,我们为CityIndex中的字段指定查找值进行查找时,首先设置TTable部件的IndexName属性为CityIndex,然后再进行查找,下面是具体的程序代码:

Table1.IndexName := 'CityIndex';

Table1.Open;

Table1.SetKey;

Table1.FieldByName{'City').AsString := Edit1.Text;

Table1.GotoKey;

使用Find方式:使用Find方式在数据库中进行数据查找的方法有:FindNearest方法和FindKey方法。

FindKey方法和FindNearest方法为数据查找提供了一个简单的方法,它们将SetKey、指定查找值、执行查找三个步骤融合在一步里完成,它们在指定查找值时,是把各字段的查找值组成一个数组传给FindKey或FindNearest。下面是使FindKey方法的一个例子。

假设Tabel1对应的表中的第一个字段是关键字段。

Table.FindKey([Edit1.Text]);

如果用GotoKey方法完成这一功能则需要编写下面代码:

Table1.SetKey;

Table1.Fields[0].AsStrine := Edit.Text;

Table1.GotoKey;

FindKey方法和FindNearest方法的区别与GotoKey和GotoNearest方法的区别是一样的。

15.4.2.3 创建主要──明细数据库应用

TTable部件中MasterSource属性和MasterFields属性是用于定义两个数据库表的一对多的关系。MasterSource属性指定主表对应的TDataSource部件,MasterFields属性指定主表和明细表之间建立联系的字段,主表和明细表之间建立一对多关系时,可能不只是基于一个字段,可能有多个字段。如果有多个字段,那么在说明MasterFields属性时,多个字段之间要用分号隔开。如Table1.MasterFields := 'OrderNo;CustNo'。在设计阶段可以使用字段连接设计器(Field Link Designer)为两上表创建一对多的关系,在Object Inspector 中双击TTable部件的MasterFields便可以打开Field Link Designer,进行一对多关系的创建。 如创建Customer.DB表和Order.DB表之间的一对多关系时,使用Field Link Designer 如图15.5所示。

图15.5 使用Field Link Designer创建一对多关系

Field Link Designer提供了一种可视化的方法来创建主要──明细表之间的一对多关系。图中Available Indexes组合框中存放着明细表中的关键字段和索引字段,可以选择索引字段进行连接。在主表中选择一个用于连接的关键字段,然后将其与明细表中相应的关键字段连接,单击Add按钮,主要──明细表的连接字段将显示在Joined Fields列表框中,如:

CustNo->CustNo

15.5 TDataSource部件及其应用

TDataSource部件是开发数据库应用程序中用到的非常重要的部件,它是连接数据集部件TTable或TQuery和数据浏览部件的桥梁。TDataSource部件本身十分简单,它所拥有的属性、事件和方法都比较少,在使用该部件时无需作太多的工作,它主要是为数据浏览部件服务的,如果在应用程序中没有使用数据浏览部件,我们也没有必要为应用程序设置TDataSource部件。

15.5.1 TDataSource部件的属性

TDataSource部件除了其他部件都拥有的Name属性和Tag属性之外,主要有下面几个属性:

DataSet属性:该属性说明TDataSource部件从中获取数据的数据集的名字,它可以是TTable部件的名字,也可以是TQuery部件的名字,甚至还可以指定其他窗体内的数据集作为该属性的值,如在下面的程序中我们指定窗体Form2中的table1作为窗体Form1中的DataSource1的DataSet属性值:

TForm1.Formcreate(Sender : Tobject);

Begin

DataSource1.DataSet := Form2.Table1;

end;

Enable属性:Enable属性可以暂时性地切断TDataSource部件和与之相连的数据集部件的连接。这是一个布尔型变量。当它的值为False时,TDataSource部件和数据集部件的连接被切断,且所有与TDataSource部件相连的数据浏览部件中将变为一片空白,不显示任何数据信息。当Enabled的值变为True时,TDataSource部件和数据集部件的连接恢复,且与TDataSource部件相连的数据浏览部件恢复显示数据。不过要实现上述这些功能,一般不使用TDataSource部件的Enabled属性,而是调用数据集部件的DisableControls方法和EnableControls 方法,因为调用这两个方法可以方便地控制与数据集部件相连的所有TDataSource部件以及与TDataSource部件相连的数据浏览部件。

AutoEdit属性:这是一个布尔型变量,它用于说明是否将与TDataSource部件相连的数据集置于编辑状态。当AutoEdit的值为True时,应用程序运行时,与TDataSource相连的数据集部件自动地被设置成编辑状态,当用户在与TDataSource部件相连的数据浏览部件中输入新的值时,数据集部件中的记录也随之改变。如果AutoEdit的值为False,用户想通过数据浏览部件或程序修改数据集中的记录,必须要调用数据集部件的Edit方法,将其置为编辑状态之后才能够进行。

15.5.2 TDataSource部件的事件

TDataSource部件具有三个事件:

OnDataChange事件

OnStateChange

OnUpdataData

OnDataChange事件:当与TDataSource相连的数据集中的记录指针的位置发生改变时,该事件就被触发,也就是说当程序调用数据集部件的Next、Previous、Insert、Append等方法导致记录指针的位置发生改变时,便会触发该事件。该事件一般用于保持应用中多个部件之间的同步。

OnUpdataData事件:当数据集部件中当前记录将要被修改时,触发该事件。例如在程序调用post方法之后但在修改后的数据记录真正被写回磁盘中的数据库文件之前触发该事件,在应用中使用非数据浏览部件时要它与数据集保持同步时常使用该事件进行相关的处理。

OnStateChange事件:当与TDataSource部件相连的数据集部件的状态发生改变时, 便触发该事件。因为数据集部件的State属性标明了数据集部件当前所处的状态,当数据集的状态发生变化时,使用该事件进行有关的处理是很有用的,在一个具体的应用中, 数据集部件的状态常常是频繁地变化的,为了跟踪数据集部件的状态变化, 可以用下面例子中的程序代码将数据集部件当前的状态显示在一个标签上:

TForm1.DataSource1OnStateChange(Sender : Tobject);

var

S : String;

begin

Case Table1,State of

dsInactive : S := 'Inactive';

dsBrowse : S := 'Browse';

dsEdit : S := 'Edit';

dsInsert : S := 'SetKey';

dsSetKey : S := 'SetKey';

end;

Label1.Caption := S;

end;

类似地我们也可以通过检测数据集部件的状态来控制有关的按钮和菜单项是否有效。 例如:在一个应用窗体中有一个InsertBtn按钮,用于控制向数据集部件table1对应的数据库表中插入记录;还有一个CancelBtn按钮用于控制是否取消用户对当前记录的修改或插入新记录。下面的程序代码根据Table1的状态来控制这两个按钮的功能(是否有效,在窗体是否变灰暗)。

Form1.DataSource1OnStateChange(Sender : Tobject);

begin

InsertBtn.Enabled := (Table1.State = dsBrowse);

CancelBtn.Enabled := Table1.State in [dsInsert,dsEdit,dsSetKey]

end;

上面的代码中,当Table1处于浏览状态(Browse状态时), 用户是不能够向数据库表中插入新记录的,此时InsertBtn按钮将变灰暗即无效。当Table1不处于Browse状态时,InsertBtn按钮有效,用户是可以向表中插入新记录。同理,只有当Table1处于特入状态(Insert状态)或编辑状态(Edit状态)或查找状态(SetKey状态)时,CancelBtn按钮才有效,也即用户可以取消当前插入的记录、修改当前的记录以及查找到的结果等。

15.6 字段部件和字段编辑器的使用

字段部件有时又称字段对象它对应着数据库表中的列即字段,字段对象是不可见的部件,在Delphi中有两种方式创建字段部件:

①在应用程序运行过程中,随着数据集部件被激活,对应于数据库表中每一列的字段部件便动态地被创建。

②在设计过程中,程序设计人员利用字段编辑器(Fields Editor)可以创建永久性的字段部件,即使字段对象对应的数据库表的结构发生了变化时,这些字段部件也不会发生变化。

既然字段部件是对应于数据库表中的各个字段的,而数据库表中的字段有多种数据类型,所以字段部件相应也有多种类型,字段部件的类型与数据库表中的字段的数据类型的对应关系如表15.5所示。

表15.5 字段部件的类型

━━━━━━━━━━━━━━━━━━━━━━━━━━━━

字段部件的类型 对应的数据类型

────────────────────────────

TStringField 字符串类型的字段

TSmallIntField 短整数类型的字段 -32768-32767

TIntegerField 整数类型的字段

TWordField 正整数类型的字段0-65535

TBooleanField 布尔型字段

TFloatField 浮点数类型的字段

TCurrenCyField 货币型字段

TDataField 日期型

TTimeField 时间型

TBCDField 小数位数固定的浮点数

TDataTimeField 日期时间型字段

━━━━━━━━━━━━━━━━━━━━━━━━━━━

我们在本书中只介绍一些常见类型的字段部件的使用,其他类型字段部件的使用可以参看联机帮助文件。