zoukankan      html  css  js  c++  java
  • delphi 三层开发经验汇总[转]

    delphi很早就已经可以进行三层的开发了. 但一直到现在, 网上这方面的东西太少 了. 要么太老, 甚至不完全正确.  例如:
    如 何进行多表更新(提交), 很多回复还是说用ADOConnection的事务, (如果要同时更新SQL SERVER和Oracle数据库的表怎么 办).
    当然也有说用SetComplete, SetAbort的. 但说的很简单, 其中的注意事项又是什么呢. 还有的推荐看李维的那本分布 式开发的书. 那本书中是写了很多值得学习和了解的东西. 但有些东西已经过时了或者说的不详细, 例如win2000中已经有COM+了. COM+已 经整合了mts及其它一些东西了. 另个, 也有些贴子上有人说, 应用服务器上只用一个TDataSetPrivoder和一个 TDataSet(ADO, Query, DBXQuery之一)就可以了. 但具体怎么用呢?又看不到下文. 
    另外, 还有一些朋友 在用第三方的一些控件. 我看了一下, 大多就像个嵌入到IE中的ActiveX, 感觉也没有什么意思---直接弄个ActiveX到IE中就不行了.

    1. 如何只用一个TDataSetPrivoder和一个TADOQuery.

    delphi的TRemoteDataModule集成了IAppServer接口.并且实现了IAppServer接口的方法:

    { IAppServer }  
     function AS_GetProviderNames: OleVariant; safecall;  
     function AS_ApplyUpdates(const ProviderName: WideString; Delta: OleVariant;  
       MaxErrors: Integer; out ErrorCount: Integer;  
       var OwnerData: OleVariant): OleVariant; safecall;  
     function AS_GetRecords(const ProviderName: WideString; Count: Integer;  
       out RecsOut: Integer; Options: Integer; const CommandText: WideString;  
       var Params, OwnerData: OleVariant): OleVariant; safecall;  
     function AS_DataRequest(const ProviderName: WideString;  
       Data: OleVariant): OleVariant; safecall;  
     function AS_GetParams(const ProviderName: WideString; var OwnerData: OleVariant): OleVariant; safecall;  
     function AS_RowRequest(const ProviderName: WideString; Row: OleVariant;  
       RequestType: Integer; var OwnerData: OleVariant): OleVariant; safecall;  
     procedure AS_Execute(const ProviderName: WideString;  
       const CommandText: WideString; var Params, OwnerData: OleVariant); safecall;  

    在应用服务器上, 只需要用这几个方法就可以了. 
    示例:unit Unit2;

    {$WARN SYMBOL_PLATFORM OFF}  
    interface  
    uses  
      Windows, Messages, SysUtils, Classes, ComServ, ComObj, VCLCom, DataBkr,  
      DBClient, Project1_TLB, StdVcl, ADODB, DB, Provider, ActiveX;  
    type  
      TMyRDM = class(TRemoteDataModule, IMyRDM)  
        ClientDataSet1: TClientDataSet;  
        DataSetProvider1: TDataSetProvider;  
        ADOConnection1: TADOConnection;  
        ADOQuery1: TADOQuery;  
        procedure RemoteDataModuleCreate(Sender: TObject);  
      private  
        I: Integer;  
        Params: OleVariant;  
        OwnerData: OleVariant;  
        // ÊÖ¹¤¼ÓÈë  
        function InnerGetData(strSQL: String): OleVariant;  
        function InnerPostData(Delta: OleVariant): Integer;  
      protected  
        class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;  
        // ÀàÐÍ¿â¼ÓÈëµÄ·½·¨  
        function GetData(const TableName, Where: WideString): OleVariant; safecall;  
        function PostData(const TableName: WideString; Delta: OleVariant): SYSINT;  
          safecall;  
      public  
        { Public declarations }  
      end;  
    implementation  
    {$R *.DFM}  
    class procedure TMyRDM.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string);  
    begin  
      if Register then  
      begin  
        inherited UpdateRegistry(Register, ClassID, ProgID);  
        EnableSocketTransport(ClassID);  
        EnableWebTransport(ClassID);  
      end else  
      begin  
        DisableSocketTransport(ClassID);  
        DisableWebTransport(ClassID);  
        inherited UpdateRegistry(Register, ClassID, ProgID);  
      end;  
    end;  
    function TMyRDM.GetData(const TableName, Where: WideString): OleVariant;  
    const SQL = 'select * from %s where %s';  
    begin  
      Result := Self.InnerGetData(Format(SQL, [TableName, Where]));  
    end;  
    function TMyRDM.PostData(const TableName: WideString;  
      Delta: OleVariant): SYSINT;  
    var  
      KeyField: TField;  
    begin  
      ClientDataSet1.Data := Delta;  
      if ClientDataSet1.IsEmpty then Exit;  
      { 
        ÕâÀï¼ÙÉèÿ¸ö±í¶¼ÓÐÒ»¸öAutoID×Ö¶Î, ²¢ÇÒÖµÊÇΨһµÄ. 
        Ò²¿ÉÒÔ¸ù¾Ý±íÖÐ, ¸Ä³ÉÏàÓ¦µÄÖ÷¼ü×Ö¶ÎÃû. 
      }  
      KeyField := ClientDataSet1.FindField('AutoID');  
      if KeyField=nil then raise Exception.Create(' ¼üÖµ×Ö¶Îδ·¢ÏÖ.');  
      if KeyField.IsNull then  
      begin  
        ADOQuery1.SQL.Text := 'select * from '+TableName+' where 1>2';  
      end  
      else  
      begin  
        ADOQuery1.SQL.Text := 'select * from '+TableName+' where AutoID='+QuotedStr(KeyField.AsString);  
        ADOQuery1.Open;  
        with ADOQuery1.FieldByName('AutoID') do ProviderFlags := ProviderFlags + [pfInKey];  
        DataSetProvider1.UpdateMode := upWhereKeyOnly;  
      end;  
      ADOQuery1.Open;  
      Result := InnerPostData(Delta);  
    end;  
    function TMyRDM.InnerGetData(strSQL: String): OleVariant;  
    begin  
      // ±ØÐëÊÇCLOSE״̬, ·ñÔò±¨´í.  
      if ADOQuery1.Active then ADOQuery1.Active := False;  
      Result := Self.AS_GetRecords('DataSetProvider1', -1, I, ResetOption+MetaDataOption,  
        strSQL, Params, OwnerData);  
    end;  
    function TMyRDM.InnerPostData(Delta: OleVariant): Integer;  
    begin  
      Self.AS_ApplyUpdates('DataSetProvider1', Delta, 0, Result, OwnerData);  
    end;  
    procedure TMyRDM.RemoteDataModuleCreate(Sender: TObject);  
    begin  
      Self.ADOQuery1.Connection := Self.ADOConnection1;  
      Self.DataSetProvider1.DataSet := Self.ADOQuery1;  
      Self.DataSetProvider1.Options := Self.DataSetProvider1.Options + [poAllowCommandText];  
      Self.ADOConnection1.Open;  
    end;  
    initialization  
      TComponentFactory.Create(ComServer, TMyRDM,  
        Class_MyRDM, ciMultiInstance, tmApartment);  
    end.  

    说白点, 就是根据client端传来的表名. 动态的改变应用服务上的ADOQuery中的语句即可. 上面的例子中, 应用服务上只加入了两上方法 GETDATA, POSTDATA两个方法, 也可以为每个表都加入对应的GET和POST方法, 那么就不需要TABLENAME参数了.对于各户端 来说, 只知道应用服务中IAPPSERVER接口加入的方法就行了.

    发现一个问题.用ADO连接到SQLSERVER时时候, 如果有类似下面的代码:
    ADOQuery1.SQL.Text := 'update table set f1=:f1, f2=f2, f3:=f3';
    ADOQuery1.Parameters[0].Value := 'a';
    ADOQuery1.Parameters[0].Value := 'b';
    ADOQuery1.Parameters[0].Value := 'c';
    设 置每一个参数值的时候.会有
    update table set f1=' ' where 1=2
    update talbe set f2=' ' where 1=2
    update talbe set f3=' ' where 1=2
    在 SQLSERVER被编译. (我通过事件探查器发现的, 即使没有执行ADOQuery1.ExecSQL)
    所以建议在应用服务器上直接拼成 (update table set f1=a, f2=b, f3=c)来执行会更好一些.
    如果有image字段.改成text类型更 好一些.(好拼SQL语句).
    客户端尽量多做一些工作,减轻应用服务器的负担也是应该考虑的.客户端给应用服务器的数据,尽量是 DELTA. 即有字段信息,也有数据信息,客户端也可以少写些代码.
    当前手上的程序.应用服务器上有两上COM+对象, 一个是用来查 询, 一个是用来更新.效率还是很好的.

    在COM+/MTS中的一点注意事项。
    SetAbort并不同于RollbackTrans 
    SetComplete不 同于CommitTrans
    SetAbort并不会导致事务立即回滚。只有在根对象失活(不一定是销毁)的时候,如果有一个对象调用了 SetAbort,才会回滚事务。
    假如A对象创建了B对象,B创建了C对象。 也就是只有在A对象失活的时候。会检查是不是有对象进行了 SetAbort.如果有,就会取消执行的操作。
    另外。在Create中进行某些初始操作要注意COM+被放到池中的情况,最好在 OnActivate, OnDeactivate中进行。
    有时候COM+对象的Create文件根本不会被调用。
    如果 你的COM+对象一定要在事务所中执行。调用IsInTransaction,如何不返回true, 就终止。

    COM+对象在建立的时候,可以指定一个事务环境。
    所以 应用服务器上有两上COM+对象, 一个是用来查询, 一个是用来更新.
    可以更 好的考虑考虑了。

    多表更新.只要将DataSetProvider.ResolveToDataSet:=True;就可以了.说明是使用ADO的驱动完成多表更新.

    待续............

    来源:http://blog.csdn.net/aroc_lo/archive/2010/02/22/5318107.aspx

  • 相关阅读:
    hdu 1863 畅通工程
    poj 2524 Ubiquitous Religions
    04 Linux终端命令01
    05 linux中yum源报错
    Filterg过滤器和Listener监听器
    03 Centos的文件目录、远程连接工具及快照操作
    02 安装虚拟机以及设置虚拟机网卡信息
    01VM虚拟机介绍及配置虚拟机网卡信息
    Jstl表达式
    EL表达式
  • 原文地址:https://www.cnblogs.com/railgunman/p/1889682.html
Copyright © 2011-2022 走看看