zoukankan      html  css  js  c++  java
  • clientdataset的使用

     
    clientdataset的使用(一)

    Delphi做为一个快速应用开发工具,深受程序员的喜爱。其强大的组件功能,让程序员能够轻松、

    高效地完成常见的界面开发、数据库应用等功能。然而,帮助的相对缺乏,使得许多组件的功能并

    不为人们正确地使用,究其原因,仍然是认识上的问题。对于MIDAS开发中的核心部件,

    TClientDataSet 和TDataSetProvider,由于资料的缺乏,人们在网上大多谈论的是李维的书籍内容

    。我有幸在BDN上见到了Cary Jensen的Professional Developer系列文章,详细阐述了DELPHI的数

    据库开发技术。现节选出其中的ClientDataSet部分,与大家共同分享。

    ClientDataSet是一个功能强大的类,通过在内存中模拟表格,实现了其它数据集组件所不具备的强

    大功能。以往只在Delphi和C++ Builder企业版中才提供这个组件,如今,Borland的全部产品(包

    括最新的Kylix)都集成了TClientDataSet组件。

    TClientDataSet从类的继承关系上来看,是TDataSet这个抽象类的子类,所以我们可以在TDataSet

    这个抽象层次上对其进行我们熟悉的操作,比如导航、排序、过滤、编辑。要注意的是,

    TClientDataSet使用了一种全新的技术,它将所有的数据均放在内存中,所以 TClientDataSet是个

    只存在内存中的“虚拟表”,因此对数据库的操作是非常快的。在PIII 850,512MB的机器上对十万

    条记录进行建索引的操作,花费的时间少于半分钟。

    与一般的数据集组件不同,TClientDataSet使用的技术比较特别,本着高速度、低存储需求的原则

    ,TClientDataSet的内部使用了两个数据存储源。第一个是其Data属性,这是当前内存数据的视图

    ,反映了所有的数据改变。如果用户从数据中删除一条记录,则此记录将从Data中消失,相应地,

    加入一条新记录后,此记录便存在Data属性中了。

    另一个数据源是Delta属性,故名思义,即增量的意思,这个属性反映了对数据的改变。无论是向

    Data属性新增还是删除记录,都会在Delta中记录下来,如果是修改了Data中的记录,则会在Delta

    保存两条相应的记录,一条是原始记录,另一条仅包含修改的字段值。正因为Delta的存在和 

    TClientDataSet在内存中记录数据的特点,所有的改变都没有立即更新加对应的物理存储中,可以

    根据这些信息在适当的时候恢复,所以 TClientDataSet天生具有缓冲更新功能。

    为了使数据更新回数据存储源,我们要调用TClientDataSet中对应的方法。如果ClientDataSet与 

    DataSetProvider关联,那么仅需调用TClientDataSet的ApplyUpdates方法即可保存数据的更新,但

    如果 TClientDataSet没有对应的TDataSetProvider存在,而是直接同文件关联,那么,这种方式是

    非常有趣的,我们在 BriefCase模型中会再次讲解这个问题。此时,如果使用TClientDataSet的

    SaveToFile和LoadFromFile,都会保留着Delta。调用MergeChangeLog和ClearChanges后,Delta的

    内容才会被
    清空。只是前者是将Delta的数据同Data结合起来,将改变存储到物理介质上,而ClearChanges则是

    一股脑儿全部清空,将数据回复到原始状态。大部分的应用都是将TClientDataSet与

    TDataSetProvider结合使用的。两者联合使用的行为反映了Borland的设计宗旨,就是要提供一个面

    向分布式环境的思路。我们下面来慢慢解释。

    当我们将TClientDataSet对象的Active属性设为True或者调用其Open方法后,ClientDataSet会向

    DataSetProvider发送一个取数据包请求。于是DataSetProvider便会打开对应的数据集,将记录指

    针指向第一条记录,然后从头到尾依次扫描。对于扫描到的每一条记录,都会将其编码成一个

    variant数组,我们通常将它称之为数据包。完成扫描后,DataSetProvider 会关闭指向的数据集,

    并将所有的这些数据包传递给ClientDataSet。在我提供的演示程序中,你可以清楚地看到这种行为

    (毕竟眼见为实吗!)。程序主界面右边的DBGrid连接到一个指向数据库表的数据源,

    DataSetProvider即指向此表。当选择了ClientDataSet | Load菜单项时,你可以看到表格的数据被

    依次扫描,一旦到达最后一条记录,表格便会被关闭,右边的DBGrid被清空,而左边反映

    ClientDataSet数据的DBGrid便出显示出内存中的数据来。由于这个过程会在DBGrid上反映出来,所

    以不到1000条记录的取出时间中,大部分都浪费在屏幕的更新显示上了,你可以选择ClientDataSet 

    | View Table Loading来禁止显示,而达到加速的目的。

    在上面的描述中,我们没有提到一个重要的环节,即数据包是如何还原成表格的。那是因为

    DataSetProvider会将数据包中的元数据解码出来,根据元数据(我们可以理解为数据表的结构)便

    可以构造出与物理数据表一模一样的内存虚拟表。但要注意的是,尽管DataSetProvider指向的数据

    表可能有多个索引,但这些信息是不会放在数据包中的,换句话说,ClientDataSet当中的数据默认

    情况下是无索引的。但因为ClientDataSet具有与TDataSet一致的行为,所以我们可以在此基础上根

    据需要重建索引。

    在ClientDataSet中的数据被修改后,可以提交给物理数据表持久化这此改变。这个工作便是由

    DataSetProvider完成的。内部工作原理是:DataSetProvider创建一个TSQLResolver的实例,这个

    实例会生成要在底层数据上执行更改的SQL语句。详细地说,就是对修改日志中的每一条被删除、插

    入、更改记录生成对应的SQL语句。这个语句的生成也可以由用户控制,DataSetProvider的

    UpdateMode属性和ClientDataSet中的ProviderFlags属性都对SQL语句的生成有影响。

    当然,你也可以换一种方式,即采取同单机或C/S结构一样的数据直接操作机制,绕过SQL语句和缓

    冲更新机制来修改数据库。只需将 ResolveToDataSet属性设为True,那么DataSetProvider在持久

    化更新时便不会使用TSQLResolve,而是直接修改物理数据源。即定位到要删除的记录,调用删除语

    句,定位到修改记录,调用修改语句。我们可以对演示程序稍加修改,观察此种行为。请将演示程

    序中的 DataSetProvider的ResolveToDataSet属性由False改为True,运行。在界面中修改数据并且

    保存,你将会看到右边的导航按钮会在瞬间变得可用。

    更绝妙的是,Borland考虑到了应用的多样性,为我们提供了BeforeUpdateRecord事件,这样,当

    DataSetProvider对每个修改日志的记录进行操作时,都会触发此事件,我们可以在此事件中加入自

    己的处理,如“加密操作”、“商业敏感数据处理”等应用,从而极大地方便了程序员,让程序员

    对于数据具有完全的控制能力。分布式环境的复杂性对数据的存取提出了更高的要求,所以使用事

    务来保证数据的完整性和一致性是非常必要的,Borland考虑到了这一点,当调用ClientDataSet的 

    ApplyUpdates时,你可以传递一个整数值来指明可以容忍的错误数量。如果你的数据非常严格,则

    可以传递0值,这样,DataSetProvider在应用修改时便会打开一个事务,如果遇到错误,便会回退

    此事务,修改日志将保持原样,并且将出错的记录标记出来,最后会触发OnReconcileError事件。

    如果传递了一个大于0的数,则当出现的错误数量小于此指定值时,事务会被提交,发生错误而导致

    提交失败的记录会保留在Delta中,而提交成功的记录会从修改日志中删除。若错误数量达到指定值

    ,则事务会回退,结果同整数值为0的情况。如果值为负数,则会交所以可提交的数据都提交,不可

    提交的数据仍然保存在修改日志中,并将出错记录标记出来。

    虽然,Borland是为了满足分布式编程的需要而设计了TClientDataSet,但在其它类型的编程环境中

    使用ClientDataSet也具有积极的意义。首先,我们可以看到,由于数据均在内存中进行操作,而且

    仅在打开数据库取数据时和将修改持久到回数据库时,才有数据库开销,其它时间数据库为零,这

    样就极大地增加了数据库的负荷,让数据库服务器能满足更多用户的连接请求。其次,

    ClientDataSet具有其它数据集所不具备的许多高级功能,这为程序员进行复杂的编程提供了便利,

    可以不考虑数据库本身是否支持这此功能,而让ClientDataSet去处理这些复杂而繁琐的细节。最后

    ,ClientDataSet在数据存储和应用程序间起到一个抽象层的作用。假如你的程序使用了

    TClientDataSet,那么如果你以后要更改数据库存储机制。比如说由BDE移植到dbExpress,或者从

    ADO移植到Interbase Express,你的用户界面和数据控制部分几乎就不用改变,只需要将

    DataSetProvider指向新的数据存取组件即可。顺便说一句,由于缓冲更新的存在,用户可能非常厌

    恶调用ApplyUpdates操作,那么你可以将此调用放入AfterPost和AfterDelte中,让用户的操作更方

    便。





    clientdataset的使用(二)

    引自:

    http://blog.sina.com.cn/s/blog_5407dd05010006h4.html~type=v5_one&label=rela_nextarticle

    与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属性来指定若干个字段,让数据集按这些字段

    排序。





    clientdataset的使用(三)

    引自:

    http://blog.sina.com.cn/s/blog_5407dd05010006h5.html~type=v5_one&label=rela_nextarticle



    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引入了“内部计算字段”的概念。与一般的计算字段不同的是,内部计算字段

    的值将随其他字段的值一起存取,这样,只有当用户修改了数据才会触发OnCalcFields事件,如果

    仅仅改变了当前记录,不会触发OnCalcFields事件。也就是说,内部计算字段的值需要重新计算的

    机会大大减少。
      在处理OnCalcFields事件的句柄中,首先要判断State属性。如果State属性返回

    dsInternalCalc,此时需要计算内部计算字段的值。如果State属性返回dsCalcFields,此时需要计

    算一般的计算字段的值。
    11.4 统 计 值
      TClientDataSet增加了统计的功能,它可以基于分组自动计算总和、平均、计数、最大、最小

    值。当用户编辑数据时,这些统计值会自动跟着变化。
    11.4.1 指定统计方式
      要指定怎样进行统计,就要用到Aggregates属性。这个属性是一个TAggregates对象,它用于管

    理一组TAggregate对象。
      在设计期,可以单击Aggregates属性边上的省略号按钮打开如图11.1所示
    的编辑器。
      图11.1 管理一组TAggregate对象
      单击按钮可以增加一个TAggregate对象,单击按钮可以删减一个TAggregate对象,单击按钮可

    以把TAggregate对象前移,单击按钮可以把TAggregate对象后移。
      可以用字段编辑器专门创建一个用于表达统计值的字段,该字段的类型必须是“Aggregate”。

    Delphi 4会自动创建一个TAggregate对象,并加到Aggregates属性中。选择一个TAggregate对象,

    Object Inpector将显示该对象的属性。
      其中,Expression属性用于指定统计表达式,例如:
    Sum(Field1)
      也可以是比较复杂的表达式:
    Sum(Qty * Price) - Sum(AmountPaid)
      在表达式中,可以使用下列统计运算符:
    .Sum计算一组数据的总和。
    .Avg计算一组数据的平均值。
    .Count计算一组数据中的非空值的个数。
    .Min计算一组数据的最小值。
    .Max计算一组数据的最大值。
      除了上述几个统计运算符外,还可以使用过滤条件中所能使用的运算符,但不能嵌套。在一个

    表达式中,可以混合出现几个统计值或常量,但不能混合出现统计值和字段。
      Sum(Qty * Price){合法}
      Max(Field1) - Max(Field2){合法}
      Avg(DiscountRate) * 100{合法}
      Min(Sum(Field1)){非法,不能嵌套}
      Count(Field1) - Field2{非法,统计值和字段不能混合出现在一个表达式中}
    11.4.2 指定分组
      默认情况下,统计值是基于数据集中所有的记录计算出来的。不过,也可以针对一部分记录计

    算统计值,这就需要事先建立分组。
      前面在介绍索引时已经提到分组的概念。可以通过IndexName属性和GroupingLevel属性来选择

    使用哪个索引以及最大的分组级别。
      例如,假设有一个表是这样的:
    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字段分组,并且指定其中的第一级,程序代码应当这样写:
    Agg.Expression := 'Sum(Amount)';
    Agg.IndexName := 'SalesCust';
    Agg.GroupingLevel := 1;
    Agg.AggregateName := 'Total for Rep';
    11.4.3 怎样获取统计值
      要获取统计值,可以调用TAggregate对象的Value函数。如果统计值是基于数据集中所有的记录

    计算出来的,随时可以调用Value函数。如果统计值是基于分组计算出来的,必须保证当前记录正好

    位于该分组内。因此,在调用Value之前,最好先调用GetGroupState函数看看当前记录是否位于该

    分组内。
      要在数据控件中显示统计值,必须事先在字段编辑器中创建一个永久字段对象,该字段的类型

    必须是Aggregate。
    11.5 数 据 包
      通过Data属性可以访问客户程序从应用服务器检索到的数据。程序示例如下:
    Procedure TForm1.Button1Click(Sender: TObject);
    Begin
    ClientDataSet1.Data := ClientDataSet1.Provider.DataRequest(FilterEdit.Text);
    End;
    11.5.1 直接对Data属性赋值
      前面讲过,客户程序既可以通过IProvider接口获取数据,也可以从另一个数据集获取数据,后

    者就是通过Data属性赋值的。程序示例如下:
      ClientDataSet1.Data := ClientDataSet2.Data;
      一旦Data被赋值,就可以用标准的数据控件显示这些数据。
      注意:当从另一个数据集获取数据时,另一个数据集的日志也将被复制过来,但不包括原来的

    范围和过滤条件。
      如果要从另一个基于BDE的数据集中获取数据,可以通过数据集构件的Provider属性,程序示例

    如下:
      ClientDataSet1.Data := Table1.Provider.Data;
      如果要从一个自定义的数据集获取数据,首先要创建一个临时的TProvider构件,然后设置其

    DataSet属性指定这个自定义的数据集。程序示例如下:
    TempProvider := TDataSetProvider.Create(Form1);
    TempProvider.DataSet := SourceDataSet;
    ClientDataSet1.Data := TempProvider.Data;
    TempProvider.Free;
    11.5.2 在数据包中加入自定义的信息
      可以把自定义的信息加到数据包中。当把数据保存到文件或流中时,这些自定义的信息也将保

    存到文件或流中。如果把数据包直接赋值给另一个数据集的话,这些自定义的信息也将被复制。
      要把自定义的信息加到数据包中,可以调用SetOptionalParam函数。要从数据包中检索自定义

    的信息,可以调用GetOptionalParam。程序示例如下:
    Procedure TAppServer.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
    var
    WhenProvided: TDateTime;
    Begin
    WhenProvided := DataSet.GetOptionalParam('TimeProvided');
    ...
    End;
    11.5.3 克隆另一个数据集
      调用TClientDataSet的CloneCursor函数可以获得一个数据集的完全相同的副本。它与直接通过

    Data属性赋值是有区别的。
      区别之一:数据在两个数据集之间是共享的,修改其中一个将同时修改另一个。
      区别之二:除了数据外,CloneCursor函数还复制了一些属性和事件,这取决于Reset和

    KeepSettings参数怎样设置。
       CloneCursor函数需要传递三个参数,其中,Source参数指定源数据集,Reset参数和

    KeepSettings参数用于设置除了数据外是否还要复制下列属性和事件:Filter、Filtered、

    FilterOptions、OnFilterRecord、IndexName、 MasterSource、MasterFields、ReadOnly、

    RemoteServer、ProviderName、Provider。
      如果Reset和KeepSettings参数都设为False,源数据集的上述属性和事件都将被复制给目标数

    据集。如果Reset参数设为 True,目标数据集的上述属性和事件都将被清空。如果Reset参数设为

    False,而KeepSettings参数设为True,目标数据集的上述属性和事件不变,不过,必须保证这些属

    性和事件与克隆后的数据相容。
    11.6 与应用服务器通讯
      在多层体系结构中,客户程序通过IProvider接口与应用服务器交换数据。这一章介绍怎样在客

    户端获得IProvider接口、怎样向应用服务器传递参数、怎样向应用服务器请求数据、怎样把用户对

    数据的修改写到数据库中。
    11.6.1 怎样在客户端获得IProvider接口
      在单层应用程序以及工作在“公文包”模式下的多层应用程序中,不需要用到IProvider接口。

    而在多层体系结构中,客户程序要与应用服务器交换数据,首先必须获得IProvider接口,这就要用

    到RemoteServer属性和ProviderName属性。
      RemoteServer属性用于指定客户端的MIDAS连接构件。MIDAS连接构件又称Data Broker,用于建

    立和维护与应用服务器的连接。
      在设计期,正确设置了RemoteServer属性后,就可以在对象观察器中为ProviderName属性选择

    一个值,实际上就是选择应用服务器上的一个TProvider构件。
    11.6.2 向应用服务器传递参数
      客户程序可以向应用服务器传递参数,这些参数实际上是传递给应用服务器上的TQuery构件或

    TStoredProc构件。既可以在设计期也可以在运行期设置参数。
      在设计期,可以单击Params属性边上的省略号按钮,打开一个如图11.2所示的编辑器。
      图11.2 设置参数
      单击按钮可以增加一个参数,单击按钮可以删减一个参数,单击按钮可以把一个参数前移,单

    击按钮可以把一个参数后移。
      选择一个参数,对象观察器将显示该参数(TParam对象)的属性。
      在运行期可以调用TParams的CreateParam函数来创建一个参数。例如,下面的代码创建了一个

    参数叫CustNo,它的使用类型是ptInput,数据类型是ftInteger,它的值设为605。
    With ClientDataSet1.Params.CreateParam(ftInteger, 'CustNo', ptInput) Do
    AsInteger := 605;
      设置好参数以后,如果TClientDataset的Active属性是False,只要把Active属性设为True,这

    些参数将被自动传递给应用服务器。如果Active属性已经为True,就要调用SendParams函数把参数

    传递给应用服务器。
      注意:传递给应用服务器的参数必须与TQuery构件或TStoredProc构件的参数匹配,包括名称、

    数据类型和参数类型。
    11.6.3 怎样向应用服务器请求数据
      TClientDataSet提供了两个属性和三个方法,用于怎样向应用服务器请求数据:
      一是FetchOnDemand属性。如果这个属性设为True,TClientDataSet会根据需要自动检索附加的

    数据包,例如BLOB字段的值或者嵌套表的内容。如果这个属性设为False,程序需要显式地调用

    GetNextPacket才能获得这些附加的数据包。
      二是PacketRecords属性,用于设置一个数据包中最多可容纳的记录数,设为-1表示一个数据包

    可以容纳数据集的所有记录。
      三是GetNextPacket函数,用于向应用服务器检索下一个数据包,并把检索到的数据包添加到前

    一次检索到的数据包的后面。这个函数返回实际检索到的记录数。
      四是FetchBlobs过程,用于从应用服务器检索BLOB字段的值。如果FetchOnDemand属性设为True

    ,就没必要调用FetchBlobs函数。
      五是FetchDetails过程,用于检索嵌套表中的数据。如果FetchOnDemand属性设为True,就没必

    要调用FetchDetails函数。
    11.6.4 更新数据库
      在多层体系结构中,用户在客户端修改了数据后,需要把最新的数据写到数据库中,这就要调

    用TClientDataSet的ApplyUpdates函数。
      ApplyUpdates只需要传递一个参数叫MaxErrors,用于指定一个整数,当遇到无法更新的记录超

    过这个数时,此次更新就中止。如果 MaxErrors参数设为0,表示只要遇到一个错误更新就中止,客

    户端的日志保持不变。如果MaxErrors参数设为-1,当应用服务器发现有错误的记录,就尝试更新下

    一个记录,等所有的记录都尝试过以后才返回。
      ApplyUpdates会自动调用Reconcile函数,进而调用应用服务器上的TProvider构件的

    ApplyUpdates函数去更新远程的数据库服务器。没有被DBMS服务器认可的记录通过Reconcile返回给

    客户端,此时将在客户端触发OnReconcileError事件让您更正错误。最后,ApplyUpdates函数返回

    仍然没有被认可的记录数。
    11.7 在文件中存取数据
      要从文件中读取数据,可以调用LoadFromFile函数。LoadFromFile函数需要传递一个参数,用

    于指定文件名。文件名应包含完整的路径。如果客户程序总是从一个固定的文件中读取数据,可以

    设置FileName属性指定一个文件名,以后,当TClientDataSet引入的数据集打开时,就自动从这个

    文件中读取数据,不需要调用LoadFromFile。
      要从流中读取数据,可以调用LoadFromStream。LoadFromStream需要传递一个参数,用于指定

    一个流对象。
      注意:LoadFromFile(LoadFromStream)只能从先前用SaveToFile(SaveToStream)保存的文件中

    读取数据。
      要把数据保存到文件中,可以调用SaveToFile函数。SaveToFile需要传递一个参数,用于指定

    文件名。如果指定的文件已存在,文件中的数据将被覆盖。如果客户程序总是把数据保存到一个固

    定的文件中,可以设置FileName属性指定一个文件名,当TClientDataSet引入的数据集关闭时,就

    自动把数据保存到这个文件中,不需要调用SaveToFile。
      要把数据保存到流中,可以调用SaveToStream。SaveToStream需要传递一个参数,指定一个流

    对象。
      注意:当把数据保存到文件或流中时,日志中记载的修改仍然保留。这样,当下次调用

    LoadFromFile或LoadFromStream读取数据时,仍然可以恢复原来的数据
  • 相关阅读:
    竞赛备考建议
    谷山丰的一生
    从首个IMO季军谈起 作者 : 付云皓
    孔庆东:单刀赴高考
    LaTeX 技巧 802:国内期刊 CCT 模板编译经验
    数学书籍推荐
    翻译
    printf中用法详解
    黎活明给程序员的忠告【转】
    log4j2配置文件log4j2.xml详解(转载)
  • 原文地址:https://www.cnblogs.com/jijm123/p/11371747.html
Copyright © 2011-2022 走看看