我完全同意你的观点。如果客户端写SQL语句,中间层没发挥什么作用。这也不符合组件化的思想,同时也不符合Midas的设计思想,也不符合面向对象的思想。
我也正在做这方面的探索。我可以提供你一些代码,也许对你又帮助。如果你搞定了也告诉我。(这是李维写的《Delphi5.xADO_MTS_COM+高级程序设计篇》书的第9章)
实现M T S / C O M +更新对象
首先如同前面实现 M T S / C O M +查询对象一样,请先建立一个 ActiveX Library项目,然后在项目中建立一个 M T S / C O M +对象。但是由于这个对象是负责更新数据的,因此设定它的事务模式为“需要事务”,如图9 - 1 5所示:
接着再于项目中建立一个数据模块,并且在数据模块中加入三个 T D C O M C o n -n e c t i o n组件,分别连接到前面实现的三个 M T S / C O M +数据模块。如图9 - 1 6所示。
为什么M T S / C O M +更新对象只使用了T D C O M C o n n e c t i o n组件连接M T S / C O M +数据模块,而不像 M T S / C O M +查询对象也使用 T C l i e n t D a t a S e t组件呢?这是因为这个M T S / C O M +更新对象是从客户端接收更新数据的 M I D A S数据封包。当它收到这个M I D A S封包之后, M T S / C O M +更新对象只要再把 M I D A S数据封包直接传递给M T S / C O M +数据模块更新数据即可。 M T S / C O M +更新对象本身并不再需要使用T C l i e n t D a t a S e t组件暂时储存任何数据,这样可以加快它的执行效率。
由于M T S / C O M +更新对象也使用了数据模块,因此它也和 M T S / C O M +查询对象一样需要在析构函数和 I n i t i a l i z e方法中释放和建立数据模块对象:
d e s t r u c t o r T m t s U p d a t e C o o r O b j . D e s t r o y ;
b e g i n
i n h e r i t e d;
F M y D M . F r e e ;
e n d;
p r o c e d u r e T m t s U p d a t e C o o r O b j . I n i t i a l i z e ;
b e g i n
i n h e r i t e d;
FMyDM := TdmUpdateCoor.Create(Forms.Application);
e n d;
接着激活可视化Type Library编辑器,为这个M T S / C O M +更新对象定义方法以便让客户端调用。图 9 - 1 7显示了 M T S / C O M +更新对象对外界提供的三个方法。此外,每一个更新数据的方法都接受三个参数:第一个参数 v D a t a s是需要更新的数据;第二个参数 i M a x E r r o r是客户端可以接受的更新错误的笔数;最后一个参数是[ i n , o u t ]的参数,这个参数会由 M T S / C O M +数据模块更新数据完毕之后填入在更新数据回数据库的过程中到底发生了多少笔错误。事实上,这三个参数的意义和使用的方法和I A p p S e r v e r接口的A S _ A p p l y U p d a t e s方法是一样的。
下面的程序代码显示了这个M T S / C O M +更新对象如何把数据真正更新回数据库中,你可以看到, M T S / C O M +更新数据通过它的数据模块中的 T D C O M C o n n e c t i o n组件连接到M T S / C O M +数据模块,再调用 M T S / C O M +数据模块的U p d a t e D a t a s方法以更新数据。最后根据状态来调用 S e t C o m p l e t e和S e t A b o r t以便释放M T S / C O M +更新对象占用的资源。
p r o c e d u r e TmtsUpdateCoorObj.UpdateBooks(vDatas: OleVariant;
iMaxError: Integer;
v a r iErrorCount: Integer);
b e g i n
t r y
FMyDM.dcomcBooks.AppServer.UpdateDatas(vDatas, iMaxError,
i E r r o r C o u n t ) ;
S e t C o m p l e t e ;
e x c e p t
S e t A b o r t ;
e n d;
e n d;
p r o c e d u r e TmtsUpdateCoorObj.UpdateCDTitles(vDatas: OleVariant;
iMaxError: Integer;
v a r iErrorCount: Integer);
b e g i n
t r y
FMyDM.dcomcCDs.AppServer.UpdateDatas(vDatas, iMaxError,
i E r r o r C o u n t ) ;
S e t C o m p l e t e ;
e x c e p t
S e t A b o r t ;
e n d;
e n d;
p r o c e d u r e TmtsUpdateCoorObj.UpdatePublishers(vDatas: OleVariant;
iMaxError: Integer;
v a r iErrorCount: Integer);
b e g i n
t r y
FMyDM.dcomcPublishers.AppServer.UpdateDatas(vDatas, iMaxError,
i E r r o r C o u n t ) ;
S e t C o m p l e t e ;
e x c e p t
S e t A b o r t ;
e n d;
e n d;
从上面的程序代码中我们可以知道,在每一个 M T S / C O M +数据模块中必须定义一个方法 U p d a t e D a t a s,让M T S / C O M +更新对象调用,以便更新数据。这是因为M T S / C O M +更新对象不知道如何更新数据,这个工作应该由单个的 M T S / C O M +数据模块来进行,因此 M T S / C O M +更新对象调用了M T S / C O M +数据模块的 U p d a t e D a t a方法来实际地更新数据。这种设计除了前面说明的可以增加 M T S / C O M +应用系统的资源使用率和执行效率之外,也非常符合对象导向的设计。虽然在这个范例中M T S / C O M +更新对象似乎除了调用M T S / C O M +数据模块更新数据之外并没有做其他的工作,因此看起来似乎是多余的。但是你不要忘了,即使是在这个简单的范例中M T S / C O M +更新对象也具备了激活事务管理的功能,因此它能够让更新数据的过程保护在Two-Phase Commit的功能之中。在实际的应用系统中,M T S / C O M +更新对象可以再执行安全管理以及执行状态登录等的工作。
完成这个范例的最后一个步骤是再为每一个 M T S / C O M +数据模块加入一个U p d a t e D a t a s方法,让 M T S / C O M +更新对象调用以便把数据真正更新回每一个M T S / C O M +数据模块代表的数据表中。因此请你在 D e l p h i的集成开发环境中激活前面实现的M T S / C O M +数据模块,再激活可视化 Type Library编辑器,然后加入这个方法。例如,图 9 - 1 8便是在m t s P u b l i s h e r s这个M T S数据模块中加入 U p d a t e D a t a s的画面。
当然,M T S / C O M +数据模块也必须实现 U p d a t e D a t a s方法。由于M T S / C O M +数据模块中已经使用了 T D C O M C o n n e c t i o n、T C l i e n t D a t a S e t和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组件的 A p p l y U p d a t e s方法更新即可。
下面就是m t s P u b l i s h e r s实现U p d a t e D a t a s的程序代码:
p r o c e d u r e TmtsPublishers.UpdateDatas(vDatas: OleVariant;
iMaxErrors: Integer;
v a r iErrorCount: Integer);
b e g i n
t r y
// statements to try
dspPublishers.ApplyUpdates(vDatas, iMaxErrors, iErrorCount);
S e t C o m p l e t e ;
e x c e p t
S e t A b o r t ;
e n d;
// try/except
e n d;
从这个范例中可以看到我们如何融合 D e l p h i的M I D A S和M T S / C O M +程序设计为一体。由于 M I D A S的数据封包可以包含新增的数据、更新的数据以及删除的数据,因此上面的范例可以简单地声明一个 U p d a t e D a t a s方法就可以更新各种的数据回数据库中,程序员不再需要像 V B或V C + +一样撰写更新各种类型数据的复杂又无趣的程序代码。 M I D A S不但已经实现了这些功能,还能够更新 M a s t e r-D e t a i l类型的数据,因此我们直接使用 M I D A不但可以提高生产力,而且也不容易出错。
在上面的 M T S / C O M +更新对象中,它使用 T D C O M C o n n e c t i o n对象并且需要M T S / C O M +数据模块定义一个更新数据的方法让 M T S / C O M +更新对象调用。当然你也可以不这样做,可以直接在 M T S / C O M +更新对象中取得 M T S / C O M +数据模块的I A p p S e r v e r接口,再直接调用 I A p p S e r v e r接口的 A S _ A p p l y u p d a t e s方法。例如,我们可以重新撰写M T S / C O M +数据模块的U p d a t e B o o k s如下:
p r o c e d u r e TmtsUpdateCoorObj.UpdateBooks(vDatas: OleVariant;
iMaxError: Integer;
v a r iErrorCount: Integer);
v a r
ServerObj : IAppServer;
OwnerData : OleVariant;
b e g i n
t r y
ServerObj := FMyDM.dcomcBooks.AppServer
a s I A p p S e r v e r ;
IAppServer.AS_Applyupdates('dspBooks', vDatas, iMaxError,
iErrorCount, OnwerData);
S e t C o m p l e t e ;
e x c e p t
S e t A b o r t ;
e n d;
e n d;
使用这种方法就不需要在 M T S / C O M +数据模块中再定义方法让 M T S / C O M +更新对象调用。不过这种方法也有一些问题,那就是 M T S / C O M +更新对象必须在程序代码中写死要使用的 T D a t a S e t P r o v i d e r组件的名称。要解决这个问题,你可以使用I A p p S e r v e r接口中的 A S _ G e t P r o v i d e r N a m e s来弹性地取得 M T S数据模块中的T D a t a S e t P r o v i d e r组件的名称。在稍后的章节中将会讨论更多的实现程序代码。