现在,多层应用程序已经和其他计算机技术一样越来越多地被谈论。有很多的原因来解释这一现象。
多层应用程序和传统的客户/服务器应用程序相比,前者有更多的优点。B o r l a n d 的Multitier Distributed
Application Services Suite(MIDAS)可以帮你建立多层应用程序。本章将深入介绍多层应用程序的一般原
理,并演示如何运用这些原理建立MIDAS 应用程序。
32.1 多层应用程序的结构
我们要学习多层应用程序,首先来了解一下“层”的概念可能会有所帮助。层,直观地说,就是
具有一些特定功能的应用程序中的一层。下面是一个数据库应用程序中三个基本层:
• 数据层数据层负责存储数据。通常,它可以是一个R D B M S ,例如Microsoft SQL Server 、
O r a c l e 或I n t e r B a s e 。
• 业务层业务层负责从数据层获取适当格式的数据并执行最后的合法性检查(也叫做执行业务规
则)。业务层也就是应用服务器层。
• 表示层也叫做G U I 层,这一层负责在客户应用程序中以适当的格式显示数据。表示层总是与业
务层打交道,它从不直接与数据层打交道。
传统的客户/服务器应用程序的体系结构如图3 2 - 1 所示。注意,客户用于访问数据的程序必须安装到
每个单独的客户机器上。各个D L L 文件版本之间的不兼容,一直是客户/服务器应用程序开发的一个弱点。
还有,因为大多数业务层安装在客户上,每次更新一个业务规则的时候都要更新所有的客户机器。
多层应用程序的体系结构如图3 2 - 2 所示。使用这种体系结构,会发现它有很多强过客户/服务器应
用程序的优点。
客户
BDE, ADO 等
DBMS 客户
D B M S
服务器
I A p p S e r v e r I A p p S e r v e r
M I D A S . D L L M I D A S . D L L
图32-1 传统的客户/服务器体系结构图32-2 多层体系结构32.2 多层体系结构的优点
下面的部分列出了多层体系结构的主要优点。
1. 业务规则集中
在大多数客户/服务器系统中,每个客户应用程序都要知道业务规则。这不仅增加了可执行文件的
大小,而且还增加了软件开发人员进行版本控制的难度。如果用户A 的程序版本老于用户B 的,那么他
们就可能执行不一致的业务规则,结果导致数据的逻辑错误。把业务规则放到应用服务器上,则只需
要创建和维护一个业务规则的副本。所以,每个使用该应用服务器的用户都执行相同的业务规则。在
客户/服务器系统中,R D B M S 能够实现其中的一些功能,但是并非所有的R D B M S 都是这样的。而且,
编写存储过程会使应用程序减少适用性。使用多层结构的方法,就使业务规则不受R D B M S 的约束,
从而更容易维持数据库独立。
2. 瘦客户体系结构
除了业务规则外,传统的客户/服务器系统大多会加重数据访问层的负担。这样就增大了可执行文
件,即通常所说的胖客户。以一个访问SQL Server 数据库的D e l p h i 应用程序为例,客户需要安装访问
数据库需要的B D E 、SQL Links 或O D B C ,以及访问SQL Sever 需要的动态链接库。安装完这些文件后,
还要进行正确的配置。这样使安装过程变得非常复杂。使用M I D A S ,数据访问由应用服务器来控制,
而数据表示则由客户的应用程序来完成。这意味着只需要发布客户应用程序和一个用来访问服务器的
D L L 文件就可以了,很明显这是瘦客户体系结构。
3. 自动错误调和
D e l p h i 提供了进行错误调和的内嵌机制。在多层应用程序中,错误调和机制和缓存更新一样是必
须的。数据被复制到客户进行修改,而多个客户可能同时访问同一条记录。错误调和帮助用户决定如
何处理下载后被修改的记录。
4. 公文包模式
公文包模式相当于实际上的公文包。可以把一些重要的文件放在公文包中来回传送,在需要的时
候打开它。D e l p h i 提供的这种打包数据的方式允许不必一直与应用服务器或数据库服务器保持活动连
接。
5. 容错
如果由于不可预料的环境导致服务器不能使用,那么要是能够动态切换到备份服务器而不需要重
新编译客户或服务器端的应用程序,将是一个很好的解决办法。D e l p h i 就提供了这样的功能。
6. 负载平衡
当为多个用户部署客户应用程序时,不可避免地要大量占用服务器资源。有两种平衡网络流量的
方法:静态负载平衡和动态负载平衡。对于静态负载平衡,需要增加另一台服务器,让一半用户使用
服务器A ,另一半用户使用服务器B 。但是,如果使用服务器A 的用户比使用服务器B 的用户产生的压
力大时怎么办呢?使用动态负载平衡,可以解决这个问题。有许多动态负载平衡的算法,例如随机法、
顺序法、最少网络用户和最少网络流量等。Delphi 4 及以上版本提供了一个利用顺序法实现负载平衡
的组件。
7. 典型失误
在建立多层应用程序中,最常出现的失误是把关于数据层的不必要的知识放在了表示层。一些合
法性检查更适合放在表示层,但在多层应用程序中合法性检查是怎样执行的决定了它的性能。
例如,如果从客户向服务器发送动态S Q L 命令,那么客户应用程序必须总是与数据层保持同步。
这种方式带来更多的不稳定成分,它们需要在整个多层应用程序中被调整。如果改变了数据层上一个
表的结构,就必须更新所有发送动态S Q L 的客户应用程序,这样才能发送正确的S Q L 命令。很明显,
这限制了开发瘦客户应用程序带来的好处。另一个例子是,当客户应用程序试图控制事务的生存期时,不让业务层去分担客户的压力。在大多
数情况下,这是通过从客户调用服务器上T D a t a B a s e 实例的三个方法—B e g i n Tr a n s a c t i o n ( )、C o m m i t ( )和
R o l l B a c k ( )来实现的。这种处理方式使客户程序更加复杂,并且违反了表示层只与业务层打交道的原则。
其实表示层不必只依赖于这种方式,它应该向业务层发送更新命令,然后让业务层处理事务中的数据更
新。
32.3 典型的MIDAS 体系结构
图3 2 - 3 显示了一个典型的M I D A S 应用程序的结构。图中的重点是远程数据模块( R D M )。R D M 是从
典型的数据模块派生而来的。这个数据模块是一特殊的窗体,可以在它上面放置非可视组件。另外,
R D M 实际上是一个C O M 对象—或者更准确地说,是一个自动化对象。客户将会用到R D M 提供的功
能。
图32-3 典型的MIDAS 应用程序
我们来看一下创建R D M 时会遇到的一些选项。选择File | New | Remote Data Module 命令,D e l p h i
打开如图3 2 - 4 所示的对话框。
图32-4 Remote Data Module Wizard 对话框
32.3.1 服务器
你已经知道了一个典型的M I D A S 应用程序的组成,现在让我们再看一下D e l p h i 中的情况。我们先
了解一些设置服务器的选项。
1. 实例选项
设置实例选项可以指定允许打开多少个服务器进程。图3 2 - 5 显示了实例选项是如何控制服务器行
为的。
下面是一个C O M 服务器具有的实例选项:
• ciMultiInstance 每个访问C O M 服务器的客户都要使用相同的服务器实例。这是缺省值。这意味
着一个客户程序要等待另一个客户程序让出C O M 服务器。稍后详细介绍怎样设置线程模式的参数值,它也具有同样的功能。这相当于客户进行串行访问。所有客户必须共享一个数据库连
接;所以,TD atab ase. Hand leSh ared 属性必须设为True 。
• ciSingleInstance 每个访问COM 服务器的客户使用单独的服务器实例。这意味着每个客户应用程
序要消耗装载服务器实例所需要的资源。这相当于客户进行并行访问。如果使用这个选项,要
小心BDE 的限制,它会使这个选项失去吸引力。特别是,BDE 5.01 限制每台机器只能启动48 个
进程。因为每个客户都要启动一个服务器进程,所以最多只能同时连接48 个客户。
• ciInternal COM 服务器实例不能由外部应用程序创建。如果希望通过代理层控制对COM 对象的
访问,就需要用到这个选项。在光盘的<DE LPHI >\DE MOS\ MIDA S\PO OLER 中可以找到一个使
用该选项的例子。
还要注意,DCOM 对象的设置会直接影响对象实例模式。有关内容的详细信息参见32.8 节“部署
M IDAS 应用程序”。
图32-5 基于实例选项的服务器行为
2. 线程选项
Delphi 5 极大地增强了对线程的支持。在Delphi 4 中,EXE 服务器的线程选项是没有什么意义的。
选择线程模式只是注册一个标志,告诉COM 对象可以运行DLL 程序。在Delphi 5 下,EXE 服务器的线
程模式允许COM 对象进行线程连接,而不使用任何外部代码。下面是RDM 的线程模式可以进行的设
置:
&#8226; Single 服务器一次只能处理一个请求。使用这个选项,不必考虑线程的维护,因为服务器只运
行一个线程而COM 对象处理消息同步的细节。但是,如果计划开发一个多层系统,这是一个最
坏的选择,因为客户B 在开始工作前必须等待客户A 完成处理。很明显,这不是一个好的解决办
法,因为客户A 不能做全天的总结报表或者其他类似的精确时间的操作。
&#8226; Apartment 选择A part ment 线程模式并结合ciM ulti Inst ance 实例选项是所有方案中最好的。c iMu-l
ti Inst ance 选项使所有客户共享一个服务器实例,但是由于A part ment 选项,一个客户并不阻塞另
一个客户。使用A part ment 线程模式,可以保证RDM 的实例数据的安全,但是要使用线程同步技
术保护对一些全局变量的访问,例如P ostM essa ge()、临界区、互斥、信号量或De lphi 封装类
TMu ltiR eadE xclu sive Write Sync hron izer 等。使用BDE 数据集时最好设置这种线程模式。注意,如果
对BDE 数据集使用这种线程模式,需要在RDM 上放一个TSes sion 组件,并且设置Aut oSes sion Name
属性为True ,使BDE 遵守其内部的线程要求。
&#8226; Free 这种模式为同时并发多个会话提供了更好的适应性,但也随之带来了责任。必须注意保护
所有数据不发生线程冲突—包括实例数据和全局变量。使用ADO(Microsoft Active Data Object)
时最好设置成这种线程模式。
1108 第四部分开发数据库
下载
客户1
客户2
客户3
服务器1
服务器2
服务器3
服务器
客户1
客户2
客户3
客户1
客户2
客户3
线程2
线程1
线程3
服务器
单实例多实例
Apartment 线程模式&#8226; Both 这种设置与F r e e 模式一样有效,只有一点例外—回调是自动串行化的。
3. 数据访问选项
Delphi 5 有许多不同的数据访问选项。继续支持B D E ,因此允许使用T D B D a t a s e t 组件,如T Ta b l e 、
T Q u e r y 和T S t o r e d P r o c 等。另外,现在还可以选择A D O 支持,可以通过新增的T D a t a s e t 组件直接访问
I n t e r B a s e 数据库。
4. 广告服务
R D M 负责与客户可用的服务通信,如果客户需要T Q u e r y ,需要把T Q u e r y 随同一个T D a t a s e t P r o v i d e r
一起放在R D M 上。把T D a t a s e t P r o v i d e r 的D a t a s e t 属性设置为T Q u e r y 。以后,当客户要使用T Q u e r y 中的数
据时,通过捆绑的T D a t a s e t P r o v i d e r 就能实现了。可以设置T D a t a s e t P r o v i d e r 的E x p o r t e d 属性为Tr u e 或F a l s e ,
来控制对它客户的可见性。
另一方面,在客户不需要整个数据集而只需调用服务器的一个方法时,也可以做到。在R D M 具有
焦点时,选择Edit | Add To Interface 菜单命令,用一个标准的方法原型来填写对话框。刷新类型库后,
可以在代码中执行这个方法。
32.3.2 客户
创建好了服务器后,我们需要创建客户来访问服务器提供的服务。首先来了解一下创建M I D A S 客
户时用到的选项。
1. 连接选项
D e l p h i 中用于连接客户和服务器的是T D i s p a t c h C o n n e c t i o n 。这个基本对象是后面列出的所有连接
类型的祖先。
通过使用标准的Wi n d o w s 服务,T D C O M C o n n e c t i o n 提供安全验证的核心。对于i n t r a n e t / e x t r a n e t 应
用程序,这种连接类型尤为有用。可以在使用D C O M 时应用先前的绑定,可以轻松地使用回调和
C o n n e c t i o n P o i n t s (也可以在使用套接字时使用回调,但不能使用动态绑定)。使用这种连接类型有如下
缺点:
&#8226; 在许多场合难于配置。
&#8226; 不能很好地兼容防火墙。
&#8226; 需要在Windows 95 机器上安装D C O M 9 5 。
T S o c k e t C o n n e c t i o n 是最容易配置的连接类型。另外,它只使用一个端口进行传输,这样很受防火
墙管理员欢迎。必须运行S c k t S r v r (在< D E L P H I > \ B I N 目录下)进行配置。Delphi 4 还要求安装Wi n S o c k 2 ,
这意味着Windows 9x 客户上要安装客户版软件。但是,如果使用Delphi 5 且不使用回调,可以考虑把
T S o c k e t C o n n e c t i o n 的S u p p o r t C a l l b a c k s 属性配置为F a l s e 。这样可以使用Wi n S o c k 1 连接客户。
TO L E n t e r p r i s e C o n n e c t i o n 提供内嵌的容错和负载平衡功能。它也能够容易地把Windows 9x 机器作
为服务器。Delphi 4 中引入了一个能够进行容错和简单的负载平衡的组件( T S i m p l e O b j e c t B r o k e r ),它现
在知道怎样把Windows 9x 作为服务器。另外,它的安装步骤非常简单。
从Delphi 4 开始,也可以使用T C O R B A C o n n e c t i o n ,它相当于D C O M 的开放标准。在升级M I D A S
应用程序以允许跨平台连接时将结束使用C O R B A 。例如,M I D A S 的J a v a 客户(从B o r l a n d 单独购买)允
许使用J B u i l d e r 客户和M I D A S 服务器打交道—即使它是用D e l p h i 开发的。
T We b C o n n e c t i o n 是Delphi 5 新增的组件。这个连接组件允许M I D A S 在H T T P 或H T T P S 上进行数据
传输。使用这个组件的一些限制如下:
&#8226; 不支持任何类型的回调。
&#8226; 客户必须安装W I N I N E T. D L L 。
&#8226; 服务器端必须运行MS Internet Information Server(IIS) 4.0 或Netscape 3.6 及以上版本。
但是,如果要通过I n t e r n e t 发布应用程序或穿过不受你控制的防火墙,这些限制看起来相当值得。
注意,所有传输都默认为正确安装了T C P / I P 。一个例外是使用两台Windows NT 机器通过D C O M
进行通信。在那种情况下,可以运行D C O M C N F G ,在Default Protocols 页把想要使用的协议移到列表
的顶端,来指定一种D C O M 使用的协议。DCOM for Windows 9x 仅支持T C P / I P 协议。
2. 连接组件
从图3 2 - 3 中可以看出M I D A S 应用程序是如何在各层之间通信的。现在将介绍一些主要的属性以及
客户与服务器通信的组件。
为了让客户与服务器通信,必须使用前面列出的T D i s p a t c h C o n n e c t i o n 组件。每个组件仅有指定连
接类型的属性,但是这些设置可以指定应用服务器的位置。T D i s p a t c h C o n n e c t i o n 组件类似于在客户/服
务器应用程序中使用的T D a t a b a s e 组件。
连接上服务器后,需要一种方式来使用服务器提供的服务。这可以通过在客户上放置一个T C l i e n t -D
a t a s e t 组件并且关联上T D i s p a t c h C o n n e c t i o n 来实现。完成这一步之后,在P r o v i d e r N a m e s 属性的下拉列
表中就可以看到服务器上的提供对象。从这个方面讲,T C l i e n t D a t a s e t 组件类似于客户/服务器应用程序
中的T Ta b l e 组件。
通过T D i s p a t c h C o n n e c t i o n 的A p p S e r v e r 属性,可以调用服务器上自定义的方法。例如,下面这行代
码调用服务器上的L o g i n ( )函数,传递两个字符串参数并返回一个布尔值:
32.4 用MIDAS 建立应用程序
你已经知道了许多建立M I D A S 应用程序时用到的选项,现在我们通过使用M I D A S 实际创建一个
应用程序来实践这些理论。
32.4.1 设置服务器
首先建立服务器应用程序。创建完服务器后,再研究如何建立客户。
1. 远程数据模块( R D M )
R D M 是创建服务器应用程序的核心。要给一个新应用程序创建R D M ,选择File | New 菜单命令,
在对象库的M u l t i t i e r 页中双击Remote Data Module 图标。在打开的对话框中可以设置一些R D M 的初始
化选项。
R D M 的名字很重要,因为服务器应用程序的P r o g I D 由项目名和R D M 名组成。例如,如果项目名
为A p p S e r v e r ,而R D M 名为M y R D M ,那么P r o g I D 就是A p p S e r v e r. M y R D M 。根据前面的介绍,确保选
择适当的实例选项和线程选项以及其他需要的设置。
Delphi 5 的一个重要改变是在T C P / I P 和H T T P 上进行连接的安全模型。因为这些协议透过Wi n d o w s
缺省的验证处理,所以要确保在服务器上运行的对象是你指定的对象。这要通过注册标识值让M I D A S
知道你想要运行的对象。幸运的是,所有要求的操作都可使用U p d a t e R e g i s t r y 方法来完成。清单3 2 - 1 显
示了创建一个远程数据模块时D e l p h i 自动生成的代码。
清单32-1 RDM 的U p d a t e R e g i s t r y 方法
这个方法在服务器注册或撤销注册时调用。除了调用U p d a t e R e g i s t r y 方法产生C O M 特定的注册项
外,还可以调用E n a b l e X X X Tr a n s p o r t 和D i s a b l e X X X Tr a n s p o r t 方法来标识对象的安全性。
注意Delphi 5 版的TSocketConnection 组件在ServerName 属性中只显示注册过的、安全的对象。
如果根本不想执行安全检查,那么去掉SCKTSRVR 中的Connections | Registered Objects Only 菜
单选项。
2. 提供者
由于服务器应用程序负责给客户提供数据,所以必须要找到一种方式,使客户能够使用服务器提
供的格式化数据。幸运的是,M I D A S 提供的T D a t a s e t P r o v i d e r 组件能够轻松地完成这一步。
首先在R D M 上放置一个T Q u e r y 组件。如果正在使用R D B M S ,那么不可避免地还需要一个T D a t a b a s e
组件。现在,我们把T Q u e r y 捆绑到T D a t a b a s e 上,并且在S Q L 属性中设置一个简单的查询,例如select *
from customer 。然后放置一个T D a t a s e t P r o v i d e r 组件到R D M 上并通过D a t a s e t 属性捆绑到T Q u e r y 上。
D a t a s e t P r o v i d e r 中的E x p o r t e d 属性决定了提供者对客户是否可见。利用这个属性可以在运行时方便地控制
提供者的可见性。
注意虽然这部分的介绍主要集中于基于B D E 的T D B D a t a s e t 的使用,但是如果想用其他从
TDataset 派生的组件,也使用相同的原理。
3. 注册服务器
服务器应用程序建立之后,需要被注册为C O M ,使连接的客户应用程序能够访问它。第2 3 章
“C O M 和A c t i v e X ”介绍了用于M I D A S 服务器的注册项。只要运行服务器应用程序,注册设置就会被
加进去。但是,在注册服务器之前,一定先要保存项目。这可以确保前面提到的P r o g I D 能够正确生
成。
如果不愿启动服务器进行注册,那么可以在运行应用程序时在命令行传递参数/ r e g s e r v e r 。这样将
执行注册过程并立即终止应用程序运行。要删除应用程序的相关注册项,可以使用/ u n r e g s e r v e r 参数。
32.4.2 创建客户
现在已经有了服务器应用程序,现在我们来了解怎样完成客户的基本任务。我们将了解怎样检索
数据,怎样编辑数据,怎样利用客户的修改去更新数据库,怎样在数据库更新过程中处理错误。
1. 检索数据
在一个数据库应用程序的开发过程之中,把数据从服务器检索到客户以进行编辑是必需的功能。
通过把数据读到本地缓存,可以减少网络流量并缩短会话时间。在D e l p h i 以前的版本中,可以利用缓
存更新来完成这项功能。这些方法同样使用在M I D A S 应用程序中。
客户通过T D i s p a t c h C o n n e c t i o n 组件与服务器打交道,需要告诉T D i s p a t c h C o n n e c t i o n 组件服务器计
算机的名字。如果使用T D C O M C o n n e c t i o n ,可以指定完整的域名(如n t . d m i s e r. c o m )、I P 地址(如
1 9 2 . 1 6 8 . 0 . 2 )或N e t B I O S 名(如n t )。但是,由于D C O M 中的一个缺陷,在有些场合使用主机名并不可靠。
如果使用T S o c k e t C o n n e c t i o n ,要在A d d r e s s 属性中指定I P 地址或在H o s t 属性中指定F Q D N 。我们稍后将介绍T We b C o n n e c t i o n 的相关选项。
指定了服务器的所在位置后,需要为T D i s p a t c h C o n n e c t i o n 提供一种途径来识别服务器。这要通过
S e r v e r N a m e 属性来实现,把S e r v e r N a m e 属性设置为S e r v e r G U I D 属性。实际上,如果要开发通用的客户
应用程序,一定要删掉S e r v e r N a m e 属性而只用S e r v e r G U I D 属性。
注意如果使用T D C O M C o n n e c t i o n 组件,S e r v e r N a m e 列表只会显示当前机器中注册的服务器。
但是,TSocketConnection 却能够显示出远程机器中注册的应用服务器。
到了这一步,把T D i s p a t c h C o n n e c t i o n 的C o n n e c t e d 属性设为Tr u e ,就可以连接到应用服务器。
有了与服务器连接的客户,现在需要一种途径来使用在服务器上创建的提供者。这可以通过
T C l i e n t D a t a s e t 组件完成。T C l i e n t D a t a s e t 组件用于连接服务器上的提供者(这样T Q u e r y 就被连接到了提
供者上)。
首先,必须为T C l i e n t D a t a s e t 组件的R e m o t e S e r v e r 属性赋值,把T C l i e n t D a t a s e t 绑定到T D i s p a t c h -C
o n n e c t i o n 上。然后,可以查看P r o v i d e r N a m e 属性以得到一个服务器上的提供者的列表。
到了这一步,已可以打开C l i e n t D a t a s e t 。
由于T C l i e n t D a t a s e t 是从T D a t a s e t 派生来的,因此可以利用在客户/服务器应用程序中学到的使用
T D B D a t a s e t 组件的许多技术。例如,把A c t i v e 属性设为Tr u e 可以打开T C l i e n t D a t a s e t 并显示数据。这和
把T Ta b l e 的A c t i v e 属性设为Tr u e 不同,T C l i e n t D a t a s e t 实际上是从服务器获取数据。
2. 在客户上编辑数据
所有从服务器传输到T C l i e n t D a t a s e t 的记录都存储在T C l i e n t D a t a s e t 的D a t a 属性中。该属性是一个代
表M I D A S 数据包的变体。T C l i e n t D a t a s e t 知道如何把这个数据包转换成可以使用的格式。之所以把这个
属性定义为一个变体,是因为受C O M 子系统的类型集的限制。
当操作T C l i e n t D a t a s e t 中的记录时,插入、修改或删除的记录会在D e l t a 属性中产生一个副本。这使
M I D A S 能够非常高效地更新应用服务器,最终到数据库。只有修改过的记录才被返回到应用服务器。
D e l t a 属性的格式也很高效的。它为每次插入或删除保存一条记录,而每次修改时保存两条记录。
修改过的记录也以很高效的形式保存。其中第一条是修改前的记录,下一条是修改后的记录,但是只
保存记录中有变化的字段。
D e l t a 属性和D a t a 属性是兼容的。换句话说,它可以被直接赋值给另一个C l i e n t D a t a s e t 组件的D a t a
属性。这样就可以随时了解D e l t a 属性的当前值。
有几种方法可以处理T C l i e n t D a t a s e t 中的数据编辑操作。我们把它们称为change control 方法。
change control 方法可以以多种方式修改T C l i e n t D a t a s e t 中的变化。
注意TClientDataset 已被证明是很有用的组件。它还是一种在内存中保存表的好方法,而且与
M I D A S 无关。另外,它通过D a t a 和D e l t a 属性提供数据的方式,已被证明在多种O O P 模式的实
现中是很有用的。这已经超过了本章讨论的范围。但是,可以在h t t p : / / w w w. x a p w a r e . c o m 和
http://www.xapware.com/ddg 中找到相关主题的内容。
3. 撤销修改
大部分用户都使用过允许U n d o 操作的字处理器。这个操作可以撤销前面的操作并回退到最初的状
态。使用T C l i e n t D a t a s e t ,可以调用c d s C u s t o m e r. U n d o L a s t C h a n g e ( )来实现类似的功能。恢复堆栈是无限
制的,如果用户愿意,可以回退所有操作直到编辑会话的开始。传递的参数用来指定是否把指针定位
在受影响的记录上。
如果用户想一下子撤销所有的修改,有一个比重复调用U n d o L a s t C h a n g e ( )更容易的方法。可以调
用c d s C u s t o m e r. C a n c e l U p d a t e s ( )撤销一个编辑会话中的所有修改。
4. 恢复到原始状态另一个操作就是允许用户把指定的记录恢复到刚被检索出来时的状态。这可以调用c d s C u s t o m e r.
R e v e r t R e c o r d ( )来实现,这时T C l i e n t D a t a s e t 定位到要恢复的记录上。
5. 客户端事务:S a v e P o i n t
最后,S a v e P o i n t 属性提供了使用客户端事务的功能。这个属性最适合于开发“假定推测”型程序。
读取S a v e P o i n t 属性的值,可以及时存储那一点的数据的基线。用户可以在需要的时候继续编辑。在某
一点,如果用户认为数据集的基线实际上就是他或她想要的,那么保存的变量可以被赋值回S a v e P o i n t ,
T C l i e n t D a t a s e t 也回滚到相同的状态。有一点值得注意,对于复杂的情况可以有多级的S a v e P o i n t 。
警告通过调用U n d o L a s t C h a n g e ( )恢复到以前的状态,会使S a v e P o i n t 无效。例如,假设用户编
辑两条记录并发出一个S a v e P o i n t 。在这一点上,用户编辑另一条记录。但是,她用U n d o L a s t
C h a n g e ( )在一条记录上进行了两次恢复。因为T C l i e n t D a t a s e t 现在处于S a v e P o i n t 之前的状态,所
以SavePoint 是不确定的。
6. 调和数据
当修改完T C l i e n t D a t a s e t 中数据的本地副本后,需要把这些改变应用回数据库中。这可以通过调用
c d s C u s t o m e r. A p p l y U p d a t e s ( )来实现。这时,M I D A S 从c d s C u s t o m e r 中得到D e l t a 的值并把它送到应用服
务器,然后应用该数据集所选择的调和机制更新数据库。所有更新在一个事务内执行。我们将会简略
地介绍在这个过程中如何处理错误。
传递给A p p l y U p d a t e s ( )的参数指定了在更新失败并回滚所有改变之前允许的错误数。这里的错误
是指关键字错误、引用完整性错误或任何其他数据库错误。如果把这个参数指定为0 ,就表示M I D A S
不容忍任何错误。因此,如果有一个错误发生,那么所有修改都不会提交给数据库。这是最常用的设
置,因为它最接近可靠数据库的方针和原则。
但是,如果需要,可以指定一个确定的错误数目,在这个错误数之内仍然可以提交所有能够提交
的记录。极限的可能是传递- 1 给A p p l y U p d a t e s ( ),这表示M I D A S 应当提交每一条可以提交的记录,而
不管遇到多少错误。换句话说,使用这个参数时事务总会被提交。
如果要控制最终的更新过程,包括改变S Q L 语句去执行插入、更新或删除操作,可以在T D a t a s e t -P
r o v i d e r. B e f o r e U p d a t e R e c o r d ( )事件中进行。例如,当用户想删除一条记录时,你可能不想在数据库上
执行实际的删除操作,而是做一个标记告诉应用程序这条记录不能访问,以后,管理员可以查看这些
被删除的记录并提交物理的删除操作。下面的代码显示了如何处理这种情况:
可以创建任意多的查询,针对不同的因子来控制更新过程,比如U p d a t e K i n d 和D a t a s e t 的值。查看
或修改D e l t a D S 中的记录时,一定要使用相应T F i e l d 的O l d Va l u e 和N e w Va l u e 属性。使用T F i e l d . Va l u e 或
T F i e l d . A s X X X 会产生不可预知的结果。
另外,在这里可以执行业务规则或避免把记录直接送到数据库。这里发生的任何错误都会通过
M I D A S 的错误处理机制进行相应的处理,我们后面将会介绍。
一旦事务结束,就有了一个处理错误的时机。错误停止在服务器端和客户端,使用户有机会更正
操作、记录错误日志或做任何其他想做的事情。
错误首先停在D a t a s e t P r o v i d e r. O n U p d a t e E r r o r 事件上。这是进行错误处理的一个好地方,可以不需
要太多的客户干预来解决问题。
错误最后到达的目的地是回到客户,在那里可以由用户决定如何处理记录。把一个事件处理过程
赋给T C l i e n t D a t a s e t . O n R e c o n c i l e E r r o r 事件就可以了。
这是非常有用的,因为M I D A S 基于乐观的记录锁定策略。这个策略允许多用户同时操作同一条记
录。通常,在M I D A S 试图调和返回数据库的数据时会造成冲突,因为记录在检索后已经被修改。
7. 使用B o r l a n d 的错误调和对话框
幸运的是,B o r l a n d 提供了一个标准的错误调和对话框用来显示错误,如图3 2 - 6 所示。还提供了它
的源代码,所以可对它进行修改以完全适应你的需要。要使用这个对话框,首先选择File | New 菜单命
令,然后在D i a l o g s 页中双击Reconcile Error Dialog 。记住,在Autocreate Forms 列表中去掉这个单元;
否则会发生编译错误。
图32-6 错误调和对话框
这个单元的主要功能被封装在函数H a n d l e R e c o n c i l e E r r o r ( )中。函数H a n d l e R e c o n c i l e E r r o r ( )可以对
O n R e c o n c i l e E r r o r 事件进行很好的处理。事实上,在O n R e c o n c i l e E r r o r 事件中的典型操作就是调用
H a n d l e R e c o n c i l e E r r o r ( )函数。这样,应用程序允许最终用户与服务器上的错误调和过程进行交互,以
决定怎样处理这些错误。代码如下:
A c t i o n 参数的值决定了M I D A S 将如何处理这条记录。我们稍后会略微谈到其他影响正常操作的因
素。下面列出的是合法的操作:
raSkip 不更新数据库中指定的记录。忽略客户记录的改变。
r a M e rge 合并记录中的字段。对插入的记录不起作用。
raCorrect 用指定的值更新数据库中的记录。在错误调和对话框中选择了这个操作后,就可以编
辑表格中的值。如果其他用户修改了这条记录,就不能用这种方法了。
raCancel 不更新数据库中的记录。从客户的缓存中删除这条记录。
raRefresh 用数据库中当前记录的值刷新客户的缓存。
raAbort 忽略整个更新操作。
并非所有选项在任何场合都有用(因此不显示出来)。使用r a M e rg e 和r a R e f r e s h 选项时要求M I D A S 通过主键来识别记录。在I n t e r B a s e 中这是自动的,但其他R D B M S 要求在T D a t a s e t 组件上手工设置主键中
所有字段的T F i e l d . P r o v i d e r F l a g s . p f I n K e y 属性为Tr u e 。
32.5 增强应用程序的更多选择
掌握了这些基本知识后,你自然想问下面应该做什么。这部分将会深入了解M I D A S 并学习如何把
这些功能按照需要加到应用程序中去。
32.5.1 客户优化技巧
T C l i e n t D a t a s e t 在内存中存储它的所有记录,因此必须小心处理返回给T C l i e n t D a t a s e t 的结果集。最
好的办法是确保应用服务器设计得非常完善,只返回用户需要的记录。理想的解决方案很少能适应现
实生活,可以用下面的技巧减少返回客户的记录数。
1. 限制数据包
打开T C l i e n t D a t a s e t 时,T C l i e n t D a t a s e t 的P a c k e t R e c o r d s 属性设定了一次能检索的记录数。但是,
M I D A S 会为可视化组件检索足够的记录。例如,如果一个窗口上的T D B G r i d 一次能够显示1 0 条记录,
而P a c k e t R e c o r d s 的值设为5 ,那么它首次检索可以得到1 0 条记录,接下来每次得到5 条记录。如果把该
属性的值设为- 1 ,那么会检索出所有的记录。如果P a c k e t R e c o r d s 的值大于0 ,那么要通知服务器应用程
序。因为服务器应用程序必须知道每个客户的指针位置,这样才能正确地返回给客户要求的记录数。
然而,可以跟踪客户的状态—通过向服务器传送上一条记录的位置。