打印本文 打印本文 关闭窗口 关闭窗口
多层数据库开发三:创建多层应用程序
作者:武汉SEO闵涛  文章来源:敏韬网  点击数3499  更新时间:2009/4/23 18:30:19  文章录入:mintao  责任编辑:mintao
eaded”、“Free-threaded”或者“Both”。
  在“Instancing”框内选择是否根据客户的请求生成远程数据模块的多个实例,可以选“Single instance”或“Multiple instance”。
3.4.2 TMTSDataModule
  要加入一个TMTSDataModule类型的远程数据模块,使用“File”菜单上的“New”命令,选择“Multitier”页,双击“MTS Data Module”图标,弹出“MTSData Module Wizard”对话框,如图3.3所示。
  图3.3 MTS Data Module对话框
  在“Class Name”框内键入远程数据模块的类名,不必以T打头。Delphi 4将以此名生成一个TMTSDataModule的派生类,并以此名生成有关接口。例如, 假设在“Class Name”框内键入“MyDataServer”, 远程数据模块的类名就是TMyDataServer,它所实现的接口叫IMyDataServer,其祖先接口是IDataBroker。
  对于TMTSDataModule类型的远程数据模块来说,必须在“ThreadingModel”框内选择一种线程模式。可以选“Single”、“Apartment”或者“Both”。在“Transaction Attributes”框内选择事务属性:
  如选择“Requires a transaction”,每当客户访问远程数据模块的接口时,都与当前的事务是相关的。客户不可能在事务中再申请一个新的事务。
  如选择“Requires a new transaction”,每当客户访问远程数据模块的接口时,都自动开始一个新的事务。如选择“Supports transactions”,远程数据模块可以用在事务的环境中,客户访问远程数据模块的接口时必须申请一个新的事务。
  如选择“Does not support transactions”,远程数据模块不能用在事务的环境中。
  注意:MTS对象只能加入到ActiveX项目中,如果试图在一个EXE项目中加入TMTSDataModule类型的远程数据模块,Delphi 4会显示一个提示框,如图3.4所示。
  图3.4 一个提示框
3.4.3 TCORBADataModule
  要加入一个TCorbaDataModule类型的远程数据模块,使用“File”菜单上的“New”命令,选取“Multitier”页,双击“CORBA Data Module”图标,弹出“CORBA Data Module Wizard”对话框,如图3.5所示。
  图3.5 CORBA Data Module对话框
  在“Class Name”框内键入远程数据模块的类名,不必以T打头。Delphi 4将以此名生成一个TCorbaDataModule的派生类,并以此名生成有关接口。例如,假设在“Class Name”框内键入“MyDataServer”, 远程数据模块的类名就是TMyDataServer,它所实现的接口叫IMyDataServer,其祖先接口是IDataBroker。
  在“Instancing”框内指定应用服务器怎样创建远程数据模块的实例,可以选“Shared Instance”或者“Instance-Per-Client”。
  如果选“Shared Instance”,应用服务器只创建远程数据模块的一个实例来处理所有客户的请求,因此,远程数据模块必须与状态无关,换句话说,就是不能使用IProvider接口。
  如果选“Instance-Per-Client”,每当一个客户试图连接时,远程数据模块都会生成一个实例。只要客户与应用服务器的连接没有断开,远程数据模块的实例就一直存在。这种模式下,允许使用IProvider接口。唯一要考虑的问题是,客户程序有可能意外终止,导致没有正常地断开与应用服务器的连接。应用服务器为了避免不必要的资源浪费,可以定期地检查客户是否正在运行,如没有,就手工把远程数据模块的实例删掉。
  在“Threading Model”框内选择一种线程模式。可以选“Single-threaded”、“Multi-threaded”。
3.5 Provider
  远程数据模块上往往要放一个或几个TDataSetProvider或TProvider构件,用于提供IProvider接口。有时候,也可以不显式地使用TDataSetProvider或TProvider构件,而是由数据集构件如TTable、TQuery或TStoredProc的Provider属性间接地提供IProvider接口。
  显式地使用TDataSetProvider或TProvider构件的好处是,可以直接控制数据包中包含哪些信息、应用服务器怎样响应客户的请求。如果显式地使用了TDataSetProvider或TProvider构件,必须设置他们的DataSet属性指定要访问的数据集。
3.5.1 控制数据包中的字段
  要控制哪些字段包含到数据包中,首先要创建永久字段。以后,只有永久字段才加入到数据包中。如果不创建永久字段的话,数据集中的所有字段都将加入到数据包中。
  如果创建的永久字段中包含计算字段,由于计算字段的值是在运行期计算出来的,这些字段虽然也能加入到数据包中,但这些字段传递到客户端后就变成只读的。
  由于客户程序很有可能要编辑修改数据,并且要把编辑修改后的数据申请更新到应用服务器上,因此,您创建的永久字段的数量不能太少,否则,很有可能出现重复的记录。举例来说,假设有一个学生成绩表,由学号、姓名、语文成绩、数学成绩、历史成绩等字段组成,如果创建的永久字段中只包含语文成绩、数学成绩、历史成绩等字段,很有可能出现两名学生的上述成绩完全一样,也就是说有重复的记录,这是不允许的。
  如果实在不想使客户程序看到某个字段,而如果没有这个字段的话很有可能出现上述错误,这时候您可以让这个字段(TField对象)的ProviderFlags属性包含pfHidden元素,表示这个字段虽然加入到数据包中,但却是隐含的,客户看不到它。
  特别要注意的是,如果使用TQuery作为应用服务器上的数据集构件,SQL语句应当选择足够多的字段,即使客户程序并不需要这么多字段,否则,就有可能出现上述错误。
3.5.2 Options属性
  这个属性是一个集合,用于设置有关打包和传递的选项。
  如果包含poFetchBlobsOnDemand元素,表示BLOB字段一般不放到包中,除非客户端的TClientDataSet构件的FetchOnDemand属性设为True或者显式地调用FetchBlobs。
  如果包含poFetchDetailsOnDemand元素,表示嵌套表中的字段不放到包中,除非客户端的TClientDataSet构件的FetchOnDemand属性设为True或者显式地调用FetchDetails。
  如果包含poIncFieldProps元素,表示把字段的属性也放到包中,包括Alignment、MinValue、DisplayLabel、DisplayWidth、Visible、DisplayFormat、MaxValue、EditFormat、Currency、EditMask、DisplayValues等属性。
  如果包含poCascadeDeletes元素,当父表中的某条记录被删除时就把子表中的相应记录也删除。
  如果包含poCascadeUpdates元素,当父表的关键字段的值变化时自动更新子表的记录。
  如果包含poReadOnly元素,表示不允许“瘦”客户向TDataSetProvider申请更新数据。
3.5.3 在数据包中加入自定义的信息
  当客户端通过IProvider 接口调用DataRequest函数请求数据时将在应用服务器端触发OnGetDataSetPropertiesevent事件,这样,应用服务器就有机会在数据包中加入一些自定义的信息。客户端可以调用GetOptionalParam来检索这些信息。
  OnGetDataSetPropertiesevent事件是这样声明的:
  TGetDSProps = Procedure(Sender: TObject; DataSet: TDataSet; out Properties:OleVariant);
  其中,Properties参数是一个可变类型的数组,用于指定要加入的信息。Properties参数的每个元素由三部分组成:名称、值和一个布尔数。Delphi 4定义了几个标准的信息名称,它们是UNIQUE_KEY、DEFAULT_ORDER、CHANGE_LOG、SERVER_COL、CONSTRAINTS、DATASET_CONTEXT、DATASET_DELTA、LCID、BDERECORD_X、TABLE_NAME、MD_FIELDLINKS、UPDATEMODE。程序示例如下:
Procedure TAppServer.Provider1GetDataSetProperties(Sender: TObject; DataSet: TDataSet; out Properties:OleVariant);
 Begin
  Properties := VarArrayCreate([0,1], varVariant);
  Properties[0] := VarArrayOf([''''TimeProvided'''', Now, True]);
  Properties[1] := VarArrayOf([''''TableSize'''', DataSet.RecordCount, False]);
 End;
  上面这个程序中,加入了两个自定义的信息,一个叫TimeProvided,它的值是当前的日期和时间,True表示这个信息可以由客户端返回给应用服务器。另一个信息叫TableSize,它的值是数据集的记录数,False表示这个信息不可以由客户端返回给应用服务器。
  以后,当客户端申请更新数据时,TDataSetProvider的OnUpdateData事件可以读出数据包中的信息。程序示例如下:
Procedure TAppServer.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
  var WhenProvided: TDateTime;
 Begin
  WhenProvided := DataSet.GetOptionalParam(''''TimeProvided'''');
   ...
 End;
3.5.4 响应客户的数据请求
  在大多数的多层应用程序中,客户请求数据是自动进行的,应用服务器对客户请求的响应也是自动的,它自动地检索数据、把数据打包,然后把数据包传递给客户。
  应用服务器在把数据包传递给客户之前,还有机会对其中的数据进行编辑 加工,例如,可以对其中敏感的数据加密,或者基于某种条件删掉一些记录。
  TDataSetProvider或TProvider构件的OnGetData事件可以让您实现上述功能,程序示例如下:
Procedure TDBClientTest.ProviderGetData(DataSet: TClientDataSet);
Begin
  With DataSet Do
   Begin
    While not EOF Do
     Begin
      Edit;
      SensitiveData.AsString := DoEncrypt(SensitiveData.AsString);
      Post;
      Next;
     End;
   End;
End;
3.5.5 响应客户的更新请求
  客户程序通过调用ApplyUpdates 向应用服务器申请更新数据。当应用服务
器上的TDataSetProvider或TProvider构件收到客户的更新请求后,就会触发OnUpdateData事件,这样您就有机会编辑数据包(Delta属性)。退出处理OnUpdateData事件的句柄后,TDataSetProvider或TProvider构件就会把数据更新到远程服务器上。
  更新是一条记录一条记录进行的。每一条记录被更新前的一瞬间将触发BeforeUpdateRecord事件,这样您还有机会对数据进行检查和修改。如果出现错误,就会触发OnUpdateError事件。发生错误的原因通常是数据违反了服务器的纠错规则,或者另一个客户程序也修改了记录,而且正好在前一个客户已经申请更新的时候。
  上述错误既可以由应用服务器来处理,也可以回传给客户处理。有些错误可能需要用户的介入,这就要客户端在处理。
3.5.6 在更新数据库之前编辑Delta数据包
  当应用服务器端调用ApplyUpdates向远程服务器申请更新数据时将触发OnUpdateData事件,这样,应用服务器就有机会对将要更新的数据进行检查,也可以对数据进行修改。OnUpdateData事件是这样声明的:
TProviderDataEvent = Procedure(Sender: TObject; DataSet: TClientDataSet) of object;
  其中,DataSet参数代表客户程序上的TClientDataSet构件,这样就可以访问Delta属性得到当前要更新的数据包。另外还有一个重要的属性需要访问,这就是UpdateStatus属性,这个属性表示Delta数据包的更新类型。程序示例如下:
Procedure TDataModule1.Provider1UpdateData(Sender:TObject;DataSet: TClientDataSet);
Begin
  With DataSet Do
  Begin
   First;
   While not Eof Do
    Begin
    If UpdateStatus = usInserted     then
     Begin
      Edit;
      FieldByName(''''DateCreated'''').AsDateTime := Date;
      Post;
     End;
    Next;
    End;
  End;
End;
3.5.7 怎样定位记录
  在处理OnUpdateData事件的句柄中,除了可以检查和修改Delta数据包外,还可以设置怎样定位记录或者说把哪些记录更新到服务器上。
  默认情况下,应用服务器用自动生成的SQL UPDATE、INSERT或DELETE语句来把Delta数据包中写到远程服务器中,例如:
  UPDATE EMPLOYEES
   Set EMPNO = 748, NAME = ''''Smith'''', TITLE = ''''Programmer 1'''', DEPT = 52
  WHERE
   EMPNO = 748 and NAME = ''''Smith'''' and TITLE = ''''Programmer 1'''' and DEPT = 47
  除非另外指定,否则,数据包中的所有字段都将出现在UPDATE子句和WHERE部分,换句话说,就是用所有的字段去定位一条记录。不过,也可以有选择地排除一些字段,这就要用到UpdateMode属性。这个属性可以设为以下值:
.upWhereAll所有字段都用来定位记录;
.upWhereChanged只有关键字段和变化了的字段用来定位记录;
.upWhereOnly只有关键字段用来定位记录。
  不过,UpdateMode属性只能区分关键字段,但实际应用往往要复杂得多。例如,可能不想更新EMPNO字段,而且不想让TITLE和DEPT字段出现在WHERE部分,这时候就要用到字段(TField对象)的ProviderFlags属性,此属性是一个集合,可以包含下列元素:
.pfInWhere该字段将不出现在自动生成的INSERT、DELETE和UPDATE语句的WHERE部分;
.pfInUpdate该字段将不出现在自动生成的UPDATE语句的UPDATE子句;
.pfInKey这个字段将出现在因为更新失败而执行的SELECT语句的WHERE部分,SELECT语句用于选择出错的记录的当前值;
.pfHidden这个字段将用来定位字段,但对客户端是隐藏的。
  下面这个程序示例把EMPNO字段排除在UPDATE子句之外,把TITLE字段和DEPT字段排除在WHERE部分之外。
Procedure TDataModule1.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
Begin
With DataSet Do
Begin
FieldByName(''''EMPNO'''').UpdateFlags := [ufInUpdate];
FieldByName(''''TITLE'''').UpdateFlags := [ufInWhere];
FieldByName(''''DEPT'''').UpdateFlags := [ufInWhere];
End;
End;
3.5.8 在服务器端纠错
  大多数数据库管理系统(RDBMS)都实现了纠错,以保证数据的完整和一致性。所谓纠错,实际上就是预先指定一些规则,字段和记录的值必须符合这些规则。
  大多数符合SQL-92的RDBMS都支持下列纠错:
.NOT NULL字段必须有值;
.NOT NULL UNIQUE字段必须有值而且不能与其他记录重复;
.CHECK字段的值必须在一个范围内;
.CONSTRAINT在表格级对字段的值进行检查;
.PRIMARY KEY指定一个或几个字段作为关键字段;
.FOREIGN KEY指定一个或几个字段引用其他表格。
  当然,不是所有的数据库都支持上述纠错,也有的服务器还支持其他纠错。其实,许多数据库桌面系统也支持纠错,不过,在服务器端纠错的优势是,多个客户程序可以共享服务器端的纠错,而不必在每个客户程序中重复一些代码。
  应用服务器可以借用远程数据库服务器的纠错规则,对客户程序传递过来的数据进行纠错,这就要用到Constraints属性,只要把这个属性设为True(默认)。
  如果不想借用远程数据库服务器的纠错规则,应当把Constraints属性设为False。
3.6 创建客户程序的一般步骤
  在多层体系结构中,一个客户程序至少要有一个TClientDataSet构件,它的作用是引入数据集。TClientDataSet是从TDataSet继承下来的,它不需要依赖BDE。
  创建一个客户程序的一般步骤是:
  第一步是使用“File”菜单上的“New Application”命令开始一个新的项目,然后使用“File”菜单上的“New”命令,再双击“Data Module”图标加入一个数据模块。
  第二步是把一个或几个MIDAS连接构件如TDCOMConnection、TSocketConnection、TOLEnterpriseConnection、TCorbaConnection、TRemoteServer或TMIDASConnection加到数据模块上。至于究竟选择哪一种MIDAS连接构件,这取决于通讯协议。
  第三步是设置有关属性指定和连接应用服务器,这与具体的MIDAS连接构件有关。有的MIDAS连接构件还有ObjectBroker属性,可以指定一个TSimpleObjectBroker构件,这样就可以动态地选择应用服务器。
  第四步是把一个或几个TClientDataSet构件放到数据模块上,设置RemoteServer属性指定一个MIDAS连接构件,设置ProviderName属性指定应用服务器上的TDataSetResolver 或TProvider构件,这样,客户程序就可以通过IProvider接口与应用服务器通讯。
  第五步是把一个TDataSource构件放到数据模块上,设置它的DataSet属性指定TClientDataSet构件,再把一个数据控件如TDBGrid放到窗体上,设置它的DataSource属性指定TDataSource构件。至此,一个简单的客户程序创建完毕。
3.7 与应用服务器连接
  要建立与应用服务器的连接,客户程序必须使用一个或几个MIDAS连接构件,这些构件可以在构件选项板的“MIDAS”页上找到。
  不同类型的MIDAS连接构件使用不同的通讯协议,定位应用服务器的方式也不同。下面就详细介绍这几种MIDAS连接构件。
3.7.1 用DCOM来连接
  要使用DCOM方式来连接应用服务器,就要用到TDCOMConnection构件。
  TDCOMConnection构件的ComputerName属性用于指定应用服务器所在的计算机。如果ComputerName属性为空,TDCOMConn

上一页  [1] [2] [3] [4]  下一页

打印本文 打印本文 关闭窗口 关闭窗口