Delphi 2006的dbExpress是一组控件和数据存取引擎,虽然开发人员在开发数据库应用程序时是使用dbExpress的,但是在dbExpress的内部却使用了DataSnap技术。Delphi 2006的DataSnap也就是以前Delphi中的MIDAS技术,只是Delphi 2006强化了MIDAS的功能,同时也让MIDAS能够执行在Windows、Linux和.NET平台上。
DataSnap是以数据封包(Data Packet)的方式来传递数据的,当客户端应用程序使用dbExpress存取数据时,在dbExpress的内部会使用SQL语句从数据源取得结果数据集,然后把结果数据集中的数据转换为数据封包,再储存到TSimpleDataSet或是TClientDataSet的Data属性值中。而数据封包则是以OleVariant的类型来代表的,因此TSimpleDataSet/TClientDataSet的Data和Delta属性值都是OleVariant类型的数值。
而当应用程序变更了数据时,这些变更的数据便储存在TSimpleDataSet/ TClientDataSet的Delta属性值中,并且当应用程序调用ApplyUpdates方法更新数据时,Delta属性值便会传递到dbExpress的内部,再由DataSnap根据这些变更的数据自动转换为SQL语句,再执行这些SQL语句把数据更新回数据源中。图2-22便显示了这个处理的流程。
而当客户端应用程序以分段的方式存取数据时,即设定TSimpleDataSet/TClientDataSet的PacketRecords属性值为一正数,那么当用户在客户端浏览或是存取的数据不在目前的Data属性值中时,dbExpress和DataSnap便会自动地从后端数据源中存取下一个数据封包。TSimpleDataSet/TClientDataSet控件会自动调用GetNextPacket方法存取下一个数据封包,一直到所有的数据都已经存取到TSimpleDataSet/TClientDataSet的Data属性值为止,图2-23说明了以分段存取数据的流程。

图2-22 dbExpress客户端应用程序如何从数据源取得数据和处理数据

图2-23 dbExpress应用程序如何使用分段的方式存取数据
让TSimpleDataSet/TClientDataSet以分段的方式存取数据的好处是除了可以增加应用程序的反应时间之外,也可以在多人使用时降低网络的负荷。这对于多人使用的系统而言是比较好的数据存取模式。
当TSimpleDataSet/TClientDataSet以分段的方式存取数据时,它也会触发TSimpleDataSet/TClientDataSet控件的相对应的事件处理函数。例如在GetNextPacket真正地存取数据之前,它会触发TSimpleDataSet/TClientDataSet的BeforeGetRecords事件,在存取到下一数据封包之后,它又会触发AfterGetRecords事件。刚才在前面的范例中我们也是利用AfterGetRecords事件来显示目前在客户端数据的笔数。图2-24显示了分段存取数据的流程以及相对应的事件。

图2-24 分段存取数据时的过程以及触发的事件
在2.1.7节中讨论了Data和Delta属性值,并且借助范例应用程序观察到当用户变更数据之后,储存在Data和Delta属性值的变化。现在让我们以一个场景范例来说明在DataSnap中对于变更的数据的处理流程。
图2-25说明了客户端应用程序如何使用dbExpress和DataSnap传递数据以及变更数据的流程。一开始客户端应用程序要求数据源传递100笔数据,当dbExpress和DataSnap收到要求后,就向数据源取得这100笔的数据,封装成数据封包并且传递给客户端应用程序。
客户端的用户在程序中变更数据,例如用户改变了客户端中Joe的记录,把Joe的职位从Accountant改成Senior Accountant,然后调用ApplyUpdates把这笔数据封装成Delta数据封包返回给dbExpress和DataSnap,要求客户端对Joe这笔数据变更。

图2-25 多层应用系统传递数据的实例
当客户端封装变更数据的数据封包时,它是如图2-26显示的方式来封装变更数据的。首先DataSnap会把未变更之前的数据封装在数据封包中,然后再封装变更后的数据。请注意第二笔封装的数据是只有被改变过的字段才会有数值,此外在每一笔记录之中也会有一个额外的字段来说明这笔记录被变更的类型,在这个例子中Joe这笔记录是被修改的,所以它的OCCUPATION字段包含“资深会计”数值,UPDATE TYPE字段会拥有Modify数值。

图2-26 客户端传递变更过的数据响应应用程序服务器更新
从上面的说明中可以知道,对于在客户端每一笔变更的数据而言,当这些数据要传递回数据源更新时,dbExpress和DataSnap会封装两笔记录。DataSnap对于封装的第二笔记录之所以只记录被变更的字段是因为这样可以节省网络传递的资源,加快传递和执行的效率。
例如在刚才的范例中,如果DataSnap检查到要被更新的记录已经被其他的用户改变了,例如Joe这笔记录的AGE字段已经被改变成42,和原先的41不一样,此时DataSnap便会把原来客户端传送来的数据,再加入数据库中最新的这笔记录传送回客户端,要求客户端应用程序或是用户决定如何处理。图2-27是这个数据处理流程的示意图。

图2-27 应用程序服务器更新数据发生错误时的处理流程
当更新错误的数据到达客户端之后,客户端应用程序可以在OnReconcileError事件处理函数中根据应用程序服务器返回造成错误的数据来进行处理。当然,客户端应用程序也可以把这些数据呈现给用户,让用户处理这些数据,在稍后的章节中会说明如何处理dbExpress/DataSnap更新数据发生的错误(见图2-28)。

图2-28 变更数据的错误
本节解释了DataSnap的基本概念和处理数据的过程。虽然在一般的dbExpress应用程序中开发人员可能并不需要直接使用DataSnap技术,因为这些工作都由dbExpress控件集自动帮助开发人员解决了,但是在许多高级的应用中DataSnap的概念和技术却是必要的,也可以帮助开发人员解决许多的困难和调整应用程序的执行效率。在本书稍后的章节中,读者将会逐步接触到DataSnap技术,并且了解如何使用它以发挥dbExpress更大的功能。
.NET的ADO.NET采用了和DataSnap许多类似的概念,只是ADO.NET又结合了离线(offline)处理数据的功能。






