查看文章 |
多层数据库开发(十一):TClientDataSet
2008-09-03 17:38
第十一章 TClientDataSet 与TTable、TQuery一样,TClientDataSet也是从TDataSet继承下来的,它通常用于多层体系结构的客户端。TClientDataSet最大的特点是它不依赖于BDE(Borland Database Engine),但它需要一个动态链接库的支持,这个动态链接库叫DBCLIENT.DLL。在客户端,也不需要用TDatabase构件,因为客户端并不直接连接数据库。 由于TClientDataSet是从TDataSet继承下来的,所以,它支持诸如编辑、搜索、浏览、纠错、过滤等功能。由于TClientDataSet在内存中建立了数据的本地副本,上述操作的执行速度很快。也正是由于TClientDataSet并不直接连接数据库,因此,客户程序必须提供获取数据的机制。在Delphi 4中,TClientDataSet有三种途径获取数据: .从文件中存取数据。 .从本地的另一个数据集中获取数据。 .通过IProvider接口从远程数据库服务器获取数据。 在一个客户程序中,可以同时运用上述三种机制获取数据。 11.1 浏览和编辑数据 和其他数据集构件一样,可以用标准的数据控件显示由TClientDataSet引入的数据集,当然,这需要借助于TDataSource构件。 由于TClientDataSet是从TDataSet继承下来的,所以,凡是其他数据集构件支持的功能,TClientDataSet构件也大致具备。不同的是,TClientDataSet能够在内存中建立数据的副本,因此,TClientDataSet比其他数据集构件增加了一些特殊的功能。 11.1.1 浏览数据 可以用标准的数据控件显示由TClientDataSet引入的数据集。在运行期,可以调用诸如First、GotoKey、Last、Next和Prior等函数来浏览数据。 TClientDataSet也支持书签功能,可以用书签来标记某条记录,以后就可以方便地找到这条记录。 对于TTable、TQuery等数据集构件来说,只能读RecNo属性来判断当前记录的序号。对于TClientDataSet构件来说,还可以写RecNo属性,使某一序号的记录成为当前记录。 11.1.2 CanModify属性 TDataSet的CanModify属性用于判断数据集中的数据是否可以修改。CanModify属性本身是只读的,也就是说,数据是否能够修改不取决于应用程序。 不过,TClientDataSet构件有其特殊性,因为TClientDataSet已经把数据在内存中建立了副本,因此,应用程序可以决定是否允许修改数据。如果不允许用户修改数据,只要把ReadOnly属性设为True,此时,CanModify属性肯定返回False。 与其他数据集构件不同,修改TClientDataSet构件的ReadOnly属性时,不需要事先把Active属性设为True。 11.1.3 取消修改 TClientDataSet传输数据的基本单位称为数据包,当前的数据包可以由Data属性来访问。不过,用户对数据的修改并不直接反映到Data属性中,而是临时写到一个日志即Delta属性中,这样做的好处是以后随时可以取消修改。 不过,这里要说明一点,尽管用户的修改并没有反映到Data,当用户在数据控件中看到的却是最新修改的数据。如果一条记录被反复修改了多次,用户看到的只是最新的数据,但日志中却记载了多次。 要取消上一次的修改,调用UndoLastChange函数。UndoLastChange需要传递一个布尔类型的参数叫FollowChange,如果FollowChange参数设为True,光标就移到被恢复的记录上,如果FollowChange参数设为False,光标仍然在当前记录上。 ChangeCount属性返回日志中记载的修改次数。如果一条记录被反复修改了多次,每调用一次UndoLastChange能够逐级取消上一次的修改。 UndoLastChange只能取消上一次的修改,如果想一下子取消所有的修改,首先要选择一个记录,然后调用RevertRecord。RevertRecord将从日志中取消所有对当前记录的修改。 TClientDataSet还有一个SavePoint属性,它能把当前的编辑状态保存起来,以后随时可以返回当时的状态。例如,可以这样保存当前的状态: BeforeChanges := ClientDataSet1.SavePoint; 以后,可以这样来恢复当时的状态: ClientDataSet1.SavePoint := BeforeChanges; 应用程序可以保存多处状态,可以恢复其中一个状态,不过,一旦某个状态被恢复,在其之后的状态就无效。 如果要一下子取消日志中记载的所有修改,可以调用CancelUpdates函数。CancelUpdates将把日志清空,取消所有的修改。 如果LogChanges属性设为False,用户对数据的修改就会直接反映到Data属性中。 11.1.4 合并修改 要把日志中记载的修改合并到Data属性中,有两种方式,具体使用哪一种方式,取决于应用程序获取数据的机制。不过,不管是哪种机制,合并后,日志自动被清空。 对于一个从文件中获取数据的程序来说,只要调用MergeChangeLog函数,就把日志中记载的修改合并到Data属性中。不用担心其他用户同时修改了数据。 对于一个从应用服务器获取数据的程序来说,就不能调用MergeChangeLog来合并数据,而要调用ApplyUpdates函数,ApplyUpdates会把日志中记载的修改传递给应用服务器,待应用服务器成功地把数据更新了数据库服务器后,才会合并到Data属性中。 11.1.5 纠错 TClientDataSet支持纠错功能。一般情况下,需要自己建立纠错规则,以便对用户输入的数据进行纠错。 此外,如果获得了IProvider接口的话,还可以从远程服务器引入纠错规则。 有时候,客户端可能需要暂时禁止纠错,因为客户端从应用服务器检索数据是分阶段进行的,在所有的数据检索完毕之前,有些纠错规则很可能会报错。 要暂时禁止纠错,可以调用DisableConstraints,要重新允许纠错,可以调用EnableConstraints函数。DisableConstraints和EnableConstraints实际上都是作用于一个内部的计数。 11.2 索 引 使用索引有这么几个好处: .在数据集中定位记录比较快。 .能够在两个数据集之间建立Lookup或Master/Detail关系。 .可以对记录排序。 在多层体系结构中,当客户程序从应用服务器检索数据时,它同时获得了默认的索引。默认的索引叫DEFAULT_ORDER,可以使用这个索引排序,但不能修改或删除这个索引。 除了默认的索引外,TClientDataSet还对日志中记载的记录自动建立了一个副索引叫CHANGEINDEX。与DEFAULT_ORDER一样,不能修改或删除这个副索引。 另外,还可以使用数据集中已建立的其他索引,或者自己建立索引。 11.2.1 创建一个新的索引 要创建一个新的索引,可以调用AddIndex。AddIndex需要传递若干个参数: 一是Name参数,用于指定索引名。在运行期切换索引时需要用到索引的名称。 二是Fields参数,它是一个字符串,用于指定索引中的字段名,彼此之间用分号隔开。 三是Options参数,用于设置索引的选项,包含ixDescending元素表示按降序排列,包含ixCaseInsensitive元素表示大小写不敏感。 四是DescFields参数,它也是一个字符串,用于指定若干个字段名,这些字段将按照降序排列。 五是CaseInsFields参数,它的作用与DescFields参数类似,包含在CaseInsFields参数中的字段将对大小写不敏感。 六是GroupingLevel参数,用于指定分组级别,其值不能超过索引中的字段数。 下面的代码创建了一个索引: If Edit1.Text <> '' and ClientDataSet1.Fields.FindField(Edit1.Text) then Begin ClientDataSet1.AddIndex(Edit1.Text+'Index',Edit1.Text, [ixCaseInsensitive],'','',0); ClientDataSet1.IndexName := Edit1.Text + 'Index'; End; 为了避免创建一个索引,可以临时用IndexFieldNames属性来指定若干个字段,让数据集按这些字段排序。 11.2.2 删除和切换索引 要删除一个先前创建的索引,可以调用DeleteIndex并指定要删除的索引名称。注意:DEFAULT_ORDER和CHANGEINDEX不能删除。 如果建立了多个索引,可以任意选择其中的一个索引,这就要用到IndexName属性。 11.2.3 用索引把数据分组 选择了一个索引后,数据集将自动按其中的字段进行排序。这样,临近的记录往往在关键字段上含有相同的值。例如,假设有一个表是这样的: SalesRep Customer OrderNo Amount 1 1 5 100 1 1 2 50 1 2 3 200 1 2 6 75 2 1 1 10 2 3 4 200 可以看出,SalesRep字段的值有重复的。对于SalesRep字段的值为1的来说,Customer字段的值也有重复的。这就是说,可以按SalesRep字段分组,进而再按Customer字段分组。显然,这里的分组级别是不同的,按SalesRep字段建立的分组属于第一级,按Customer字段建立的分组属于第二级。实际上,分组级别取决于字段在索引中的顺序。 TClientDataSet可以决定是否按照分组级别来显示记录的值。例如,也许想以下面这种形式显示数据: SalesRep Customer OrderNo Amount 1 1 5 100 2 50 2 3 200 6 75 2 1 1 10 2 3 4 200 要判断当前记录某一级的什么位置,可以调用GetGroupState函数。GetGroupState函数需要传递一个参数,用于指定分组级别。 11.3 计 算 字 段 与其他数据集一样,也可以在TClientDataSet建立的数据集中增加计算字段。计算字段的值是基于同一个记录中的其他字段计算出来的。 在其他数据集中,只要用户修改了数据或当前记录发生改变,就会触发OnCalcFields事件,换句话说,计算字段的值就被计算一次。 TClientDataSet引入了“内部计算字段” |
最近读者: