123123

        <p>&nbsp;&nbsp;&nbsp; 传统的数据集的读取和更新,是通过中间层的TDataSetProvider来完成的。TDataSetProvider负责从它上游的数据集读取数据生成Data包,再传给客户端;另一方面,在客户端提交更新时(TClientDataSet.ApplyUpdates),TDataSetProvider还负责解析上传的Delta包,并最终实现数据库的更新。现在在我们当前的方法调用方式下,不能再通过TDataSetProvider.ApplyUpdates来自动完成更新了,但是,我们还可以借用TDataSetProvider解析Delta数据包的功能,手动生成SQL语句来完成数据库的更新。手动更新看似复杂,实际上编码并不多,而且这种方式具有很大的灵活性,同时还解决了在传统方式下,多表联合查询不能完全自动更新的软肋。</p>

 

下面来看看示例代码:

 

中间层代码

 


interface

uses
  SysUtils, Classes, DSServer, DBXOracle, FMTBcd, DB, SqlExpr, WideStrings, Provider,
  CodeSiteLogging, DBClient;

type
{METHODINFO ON}<br>&nbsp; TServerMethods1 = class(TDataModule)<br>&nbsp;&nbsp;&nbsp; SQLConnection1: TSQLConnection;<br>&nbsp;&nbsp;&nbsp; SQLDataSet1: TSQLDataSet;<br>&nbsp;&nbsp;&nbsp; DataSetProvider1: TDataSetProvider;<br><strong>&nbsp;&nbsp;&nbsp; procedure DataSetProvider1BeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);<br></strong>&nbsp; private<br>&nbsp;&nbsp;&nbsp; { Private declarations }<br><strong>&nbsp;&nbsp;&nbsp; FOnDeltaRecordUpdate: TBeforeUpdateRecordEvent;<br>&nbsp;&nbsp;&nbsp; procedure UpdateEmployeesDelta(Sender: TObject; SourceDS: TDataSet;<br>&nbsp;&nbsp;&nbsp; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var Applied: Boolean);<br></strong>&nbsp; public<br>&nbsp;&nbsp;&nbsp; { Public declarations }<br>&nbsp;&nbsp;&nbsp; function GetEmployeeFullName(EmployeeId: Integer): string;<br>&nbsp;&nbsp;&nbsp; function GetEmployees: TDataSet;<br><strong>&nbsp;&nbsp;&nbsp; function UpdateEmployees(EMPDelta: OleVariant): Boolean;<br></strong>&nbsp; end;<br>{METHODINFO OFF}

implementation


procedure TServerMethods1.DataSetProvider1BeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet;
  DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);
begin
  if Assigned(FOnDeltaRecordUpdate) then
    FOnDeltaRecordUpdate(Sender, SourceDS, DeltaDS, UpdateKind, Applied);
end;

 

procedure TServerMethods1.UpdateEmployeesDelta(Sender: TObject; SourceDS: TDataSet;
  DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);
var
  SQLStr, Clause1, Clause2: string;
  I: Integer;
begin
  Applied := False;

  case UpdateKind of
    {REGION 'Process ukModify'}<br>&nbsp;&nbsp;&nbsp; ukModify:<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with DeltaDS do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for I := 0 to Fields.Count - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if not Fields[I].IsNull then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clause1 := Clause1 + ', ' + Fields[I].FieldName + '=:' + Fields[I].FieldName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clause1 := System.Copy(Clause1, 2, Length(Clause1) - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLStr := 'UPDATE EMPLOYEES SET ' + Clause1 + ' WHERE EMPLOYEE_ID = :EMPLOYEE_ID';<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with SQLDataSet1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Close;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CommandText := SQLStr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for I := 0 to DeltaDS.Fields.Count - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if not DeltaDS.Fields[I].IsNull then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParamByName(DeltaDS.Fields[I].FieldName).Value := DeltaDS.Fields[I].Value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParamByName('EMPLOYEE_ID').AsInteger := DeltaDS.FieldByName('EMPLOYEE_ID').OldValue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ExecSQL() = 0) then Abort;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; {ENDREGION}<br>&nbsp;&nbsp;&nbsp; {REGION 'Process ukInsert'}<br>&nbsp;&nbsp;&nbsp; ukInsert:<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for I := 0 to DeltaDS.Fields.Count - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if not DeltaDS.Fields[I].IsNull then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clause1 := Clause1 + ', ' + DeltaDS.Fields[I].FieldName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clause2 := Clause2 + ', :' + DeltaDS.Fields[I].FieldName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clause1 := System.Copy(Clause1, 2, Length(Clause1) - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clause2 := System.Copy(Clause2, 2, Length(Clause2) - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLStr := 'INSERT INTO EMPLOYEES(' + Clause1 + ') VALUES(' + Clause2 + ')';<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with SQLDataSet1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Close;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CommandText := SQLStr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for I := 0 to DeltaDS.Fields.Count - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if not DeltaDS.Fields[I].IsNull then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParamByName(DeltaDS.Fields[I].FieldName).Value := DeltaDS.Fields[I].Value;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ExecSQL() = 0) then Abort;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; {ENDREGION}<br>&nbsp;&nbsp;&nbsp; {REGION 'Process ukDelete'}<br>&nbsp;&nbsp;&nbsp; ukDelete:<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLStr := 'DELETE EMPLOYEES WHERE EMPLOYEE_ID = :EMPLOYEE_ID';<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with SQLDataSet1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Close;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CommandText := SQLStr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ParamByName('EMPLOYEE_ID').AsInteger := DeltaDS.FieldByName('EMPLOYEE_ID').OldValue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ExecSQL() = 0) then Abort;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; {ENDREGION}
  end;

  Applied := True;
end;

 

function TServerMethods1.UpdateEmployees(EMPDelta: OleVariant): Boolean;
var
  ErrorCount: Integer;
begin
  //指定Employees的专有更新过程
  FOnDeltaRecordUpdate := UpdateEmployeesDelta;
  DataSetProvider1.ApplyUpdates(EMPDelta, 0, ErrorCount);
  Result := ErrorCount = 0;
end;

 

1、增加一个DataSetProvider1控件,让它与SQLDataSet1控件关联。
2、申明一个事件句柄FOnDeltaRecordUpdate: TBeforeUpdateRecordEvent,然后在DataSetProvider1的BeforeUpdateRecord事件过程中,再嵌套一个TBeforeUpdateRecordEvent事件。
3、申明和定义一个私有过程UpdateEmployeesDelta,其参数定义遵循TBeforeUpdateRecordEvent事件过程的定义,此过程专门用来处理Employees的更新。实际上,所有表的更新只有Update、Insert和Delete三种类型,针对当前传入的Delta包,可以手动构造出SQL语句。这里只有具有的表不一样,但处理流程都是完全一样的,所以,若有其他新表的更新,可以直接套用相关代码段,甚至可以做成一个专门的一个通用过程。
4、申明和定义UpdateEmployees函数,此方法是暴露给客户端进行调用的。若客户端有多个表需要在一个事务中完成更新,则可以在此过程中手动启动事务,分别完成多个表Delta数据包的更新,最后提交或rollback。若有新的方法暴露给客户端来更新其他表的时候,只需要相应地再创建一个私有更新过程,并把此过程指针赋给FOnDeltaRecordUpdate即可。

 

客户端代码:

    通过在客户端中的TSQLConection控件,可以生成DataSnap client class:

  TServerMethods1Client = class
  private
    FDBXConnection: TDBXConnection;
    FInstanceOwner: Boolean;
    FEchoStringCommand: TDBXCommand;
    FReverseStringCommand: TDBXCommand;
    FGetEmployeeFullNameCommand: TDBXCommand;
    FGetEmployeesCommand: TDBXCommand;
    FGetJobsCommand: TDBXCommand;
    FUpdateEmployeesCommand: TDBXCommand;
  public
    constructor Create(ADBXConnection: TDBXConnection); overload;
    constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload;
    destructor Destroy; override;
    function GetEmployeeFullName(EmployeeId: Integer): string;
    function GetEmployees: TDataSet;
    function UpdateEmployees(EMPDelta: OleVariant): Boolean;
  end;

 

  在调用模块中,比如一个按钮事件中完成Employees被更新数据的提交,其中FMethodProxy为TServerMethods1Client的实例:

procedure TForm2.Button2Click(Sender: TObject);
begin
  if ClientDataSet1.ChangeCount > 0 then
  begin
    FMethodProxy.UpdateEmployees(ClientDataSet1.Delta);
  end;
end;

 

通过上述方式,中间层业务类模块,只需要三个控件,就可以完成现在和今后所有的数据读取和更新操作。更重要的是,由于每次方法调用之间是不需要客户端到中间层、中间层到数据库的两个连接始终保持并专用的,在实际调用方法的时候才需要连接。但在上述例子中,我们的类工厂(TDSServerClass)的LifeCycle属性采用的是缺省的Session方式,也就是说,一旦客户端请求了一次方法调用,则此客户端与中间层、中间层与数据库的连接就一直保持着,并且只能为此客户端专用,直到此客户端关闭连接为止。客户端与中间层的连接先不说,中间层与数据库的连接也是专用的话,那数据库连接资源的使用效率就太差了。为了解决此问题,我们可以在中间层引用对象池的机制。后文将关注对象池的问题,敬请等待续集。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flybirding10011

谢谢支持啊999

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值