Delphi 多层数据库无状态模式下 ClientDataSet 分段读取服务器端记录的方法

unit UDataModule3;
{------------------------------------------------------------------------------
  ClientDataSet 分段获取数据。有状态模式下,服务器端 select 出来100条,如果设置 ClientDataSet.PacketRecords := 10,
  则 ClientDataSet 一次获取10条,ClientDataSet.GetNextPacket 获取后继 10 条。服务器端缓存住 1000 条,并帮客户端维护游标。 
  在无状态模式下,服务器端不帮客户端维护游标。而且,如果记录太多,不应该让服务器端一次取出 1000 条并缓存在服务器内存里面。

  所以,这种情况下,服务器端应该是 Select first 10 * from MyTable

  然后,客户端每次需要读取后面的数据时,就执行 ClientDataSet.GetNextPacket; 方法来向服务器请求下一段数据。
  但是,这里的问题是:第一次读取数据,服务器里面只有10条,所有10条都已经读回客户端,
  则 ClientDataSet.GetNextPacket 时不会有向服务器读取数据的动作。查其代码,里面会判断一下 ProviderEOF 属性。

  所以,这里如果服务器端没有缓冲比10条更多的数据,则必须要重新设置 ProviderEOF 属性为 False 才能让它执行 GetNextPacket。
  但是,ProviderEOF 属性是 Protected 的,在程序里无法访问。

  So, 这里必须 Hack 一下 TClientDataSet。做法是这样的:

  Type

  TMyClientDataSet = class(TClientDataSet)
  public
    property ProviderEOF;
  end;

  通过继承,把 ProviderEOF 属性公开。

  然后,在 ClientDataSet2 需要设置 ProviderEOF 属性的地方,就可以这样:
  TMyClientDataSet(ClientDataSet2).ProviderEOF := False;

  然后,再执行 ClientDataSet2.GetNextPacket; 它就会去向服务器请求下一段数据了。

  然后,服务器怎么知道从哪里开始读取下一段数据?

  在 ClientDataSet2.BeforeGetRecords 事件里面,将 OwnerData 设置为当前记录排序字段的最大值;
  在服务器端的 DataSetProvider2.BeforeGetRecords 里面,为服务器端的 TQuery 的 select 参数赋值为 OwnerData。

  这个程序的例子,排序字段就是主键字段,是一个整数编号字段。

  很多时候,排序字段可以是时间字段。

  以下代码测试通过。

  pcplayer 2016-6-23

------------------------------------------------------------------------------}
interface

uses
  System.SysUtils, System.Classes, FireDAC.UI.Intf, FireDAC.VCLUI.Wait,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.Phys.Intf,
  FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys,
  FireDAC.Phys.FB, FireDAC.Phys.FBDef, FireDAC.Stan.Param, FireDAC.DatS,
  FireDAC.DApt.Intf, FireDAC.DApt, FireDAC.Comp.Client, FireDAC.Comp.DataSet,
  Data.DB, Datasnap.DBClient, Datasnap.Provider, FireDAC.Comp.UI;

type
  TDataModule3 = class(TDataModule)
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    FDConnection1: TFDConnection;
    FDManager1: TFDManager;
    FDTransaction1: TFDTransaction;
    DataSetProvider1: TDataSetProvider;
    ClientDataSet1: TClientDataSet;
    ClientDataSet1XUHAO: TIntegerField;
    ClientDataSet1ID: TWideStringField;
    ClientDataSet1FULLNAME: TWideStringField;
    ClientDataSet1EMAIL: TWideStringField;
    DataSource1: TDataSource;
    FDQuery1: TFDQuery;
    FDStoredProc1: TFDStoredProc;
    FDQuery2: TFDQuery;
    DataSetProvider2: TDataSetProvider;
    ClientDataSet2: TClientDataSet;
    DataSource2: TDataSource;
    CldTemp: TClientDataSet;
    procedure DataSetProvider2BeforeGetRecords(Sender: TObject;
      var OwnerData: OleVariant);
    procedure ClientDataSet2BeforeGetRecords(Sender: TObject;
      var OwnerData: OleVariant);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure OpenClientDataSet2;
  end;

  //Hack 一下 TClientDataSet 以便这里能访问到其 ProviderEOF 属性。
  TMyClientDataSet = class(TClientDataSet)
  public
    property ProviderEOF;
  end;

var
  DataModule3: TDataModule3;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

{ TDataModule3 }

{-------------------------------------------------------------------------------
  这里的 FDQuery2 的 SQL 是:select first 10 * from MyTable

  这个 MyTable 里面的第一个主键字段是一个整数字段,叫做 XuHao

  这里的 ClientDataSet2.PacketRecords 是默认的 -1。因为这是无状态模式。
-------------------------------------------------------------------------------}

procedure TDataModule3.ClientDataSet2BeforeGetRecords(Sender: TObject;
  var OwnerData: OleVariant);
begin
  OwnerData := ClientDataSet2.Params[0].Value;  //将当前排序最大字段值传递给服务器。
end;

procedure TDataModule3.DataSetProvider2BeforeGetRecords(Sender: TObject;
  var OwnerData: OleVariant);
begin
  FDQuery2.Params[0].Value := OwnerData;  //给 服务器端的 select 参数赋值。
end;

procedure TDataModule3.OpenClientDataSet2;
begin
  with ClientDataSet2 do
  begin
    if not Active then
    begin
      Params[0].Value := 0;
      Open;
      Exit;
    end;

    CldTemp.CloneCursor(ClientDataSet2, True);
    CldTemp.Last;
    Params[0].Value := CldTemp.FieldByName('XuHao').AsInteger;


    TMyClientDataSet(ClientDataSet2).ProviderEOF := False;  //设置 ProviderEOF 为 False,后面的 GetNexPacket 才会被执行。
    ClientDataSet2.GetNextPacket;
  end;
end;

end.


----------------

又及:除了分段读取数据,这个方法还可以用于 ClientDataSet 已经根据某个服务器端的参数获取了一组数据,然后更换参数获取新数据,但原有数据不丢的情况。

通常做法是 Close; Params[0].Value := 'New param'; Open;但这样一来,原有数据在 ClientDataSet 中丢失了。现在可以不用做 Close 而是 GetNextPacket 的方式来获取新的参数的数据。测试通过。



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Delphi 6中,ClientDataSet的AddIndex方法用于手动添加索引。下面是使用AddIndex方法的示例代码: ``` // 假设有一个名为cds 的ClientDataSet对象 // 开始事务 cds.Active := True; cds.DisableControls; cds.IndexDefs.Update; try // 添加一个名为"IndexName"的索引 with cds.IndexDefs.AddIndexDef do begin Name := 'IndexName'; // 索引名称 Fields := 'FieldName1;FieldName2'; // 需要包含在索引中的字段名,多个字段之间使用分号分隔 Options := []; // 索引选项,可以根据需要设置选项,如Unique(唯一性)、Descending(降序)等 end; // 应用索引 cds.IndexName := 'IndexName'; // 打开数据集 cds.Open; // 在添加索引后,可以通过Locate或FindKey方法等进行定位和查找操作 finally // 结束事务 cds.EnableControls; end; ``` 上述代码首先启用ClientDataSet,然后禁用控件以提高效率。接着使用IndexDefs.AddIndexDef方法创建一个新的索引定义,并设置索引的名称、需要包含的字段以及其他选项。然后将创建的索引应用到ClientDataSet的IndexName属性中。最后打开数据集,并进行定位和查找操作。 需要注意的是,索引定义的添加和应用必须在数据集处于Active状态之前进行。另外,在使用AddIndex方法之前,建议先调用IndexDefs.Update方法更新索引定义,以确保索引定义与数据集的字段结构保持同步。 通过以上步骤,你可以在Delphi 6中使用ClientDataSet的AddIndex方法手动添加索引。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值