Delphi中帶緩存的數據更新技術
發表時間:2024-02-21 來源:明輝站整理相關軟件相關文章人氣:
[摘要]一. 概念 在網絡環境下,數據庫應用程序是c/s或者是多層結構的模式。在這種環境下,數據庫應用程序的開發應當盡可能考慮減少網絡數據傳輸量,并且盡量提高并發度。基于這個目的,帶緩存的數據更新技術應運而生,其大致過程就是:應用程序將數據庫中數據提取到客戶端的緩沖區,在緩沖區中完成數據的修改、更新、以...
一. 概念
在網絡環境下,數據庫應用程序是c/s或者是多層結構的模式。在這種環境下,數據庫應用程序的開發應當盡可能考慮減少網絡數據傳輸量,并且盡量提高并發度。基于這個目的,帶緩存的數據更新技術應運而生,其大致過程就是:應用程序將數據庫中數據提取到客戶端的緩沖區,在緩沖區中完成數據的修改、更新、以及新數據的插入等操作;等操作完成之后,在一個合適的時間,一次性的將數據提交給數據庫,從而大大減少了網絡流量,減小了數據庫服務器的負載,提高了并發性能。
應當說,這并不是太新的技術,在delphi等數據庫前端開發工具較早期版本中,就已經對這種技術做出了的支持。但是筆者發現一些程序員并不注意這種技術的合理運用,依然停留在單機應用程序的思路下,導致所編出來的程序效率低下或者出現潛在的錯誤。因此有必要對該技術的優點、運用原則以及運用方法(以delphi為例)做一個總結。
二. 優缺點
帶緩存的數據更新技術主要有如下幾個優點:
(1) 最大限度的減少了網絡流量,減少了數據存取的時間。提高了客戶端數據庫操作人員的效率。
(2) 減輕了數據庫服務器的負擔,因為許多反復的更新、修改、刪除操作可以在客戶端的緩沖區內完成,最后只把結果提交給服務器。
(3) 有效縮減事務(transaction)處理的時間,減少了并發事務的吞吐量。從而更能夠保證數據庫的一致性。
我們可以舉個例子來說明其優越性:數據庫操作員往數據庫中插入一條數據記錄,但馬上發現該記錄不合要求,于是將該數據刪除。這一過程,如果采用不帶緩沖的數據更新技術,則服務器端將執行一次插入操作,一次刪除操作,在客戶端和服務器端進行往返兩次的數據傳輸,而且如果此數據與其他的數據庫表有級聯關系,還必須考慮到數據的級聯更新、刪除、恢復等操作。如果采用帶緩存的數據更新方法,則可以在客戶端的數據緩沖區完成這兩步互逆的操作,而不會給服務器端帶來任何動作,不會產生任何網絡數據傳輸以及數據的級聯更新操作。由此可見帶緩沖的數據更新技術的巨大優勢。
帶緩存的數據更新有一個缺點就是因為數據是在客戶端存放的,所以該數據如果被其它用戶更改的話,會產生丟失修改等不一致狀況,需要考慮其并發控制的細節,充分考慮那些多變的數據可能出現的不一致的情況。這種技術的運用需要一定的技巧與一定的思維轉變。
三. 運用原則
任何技術的優越性都是在一定的環境中體現出來,帶緩沖的數據庫更新技術主要在以下幾種情況下運用更有優勢:
(1) c/s或者多層的數據庫應用的場合。這種情況下,可以有效降低網絡流量。在單機情況下,該技術沒有意義。
(2) 在多表的數據更新場合。例如主表/明細表(master/detail)結構的更新中,往往兩個表的增刪是相互影響的,那么在客戶端完成兩個表的所有更新之后,最后可以定義一個事務來提交所做的更新操作。這樣便有效的縮短事務的時間,更好的保證了數據的一致性。
(3) 服務器負載能力有限的場合。如今,隨著pc機的速度提高以及價格的降低,客戶機和服務器的能力差別越來越小,服務器的服務能力相對地下降了。客觀上要求從軟件的角度來降低服務器的負擔,而帶緩沖的數據更新則通過客戶端分擔一部分更新任務的方式,降低了服務器的負擔。
(4) 數據被同時更新的概率比較低的場合。如果數據庫中同一條數據很可能在同一時段被多個用戶更新,那么這種情況就不適合帶緩存的更新,因為在這種情況下,容易產生數據的錯誤覆蓋,從而導致數據的不一致。
四. Delphi中的控制方法概述
Delphi作為一個流行的數據庫開發工具,具有豐富的數據庫操縱功能。在帶緩沖的數據存取技術上,delphi做出了很全面的支持。通常,delphi為用戶提供了TTable和TQuery等幾種存取數據庫表的數據集控件;提供了TDBNevigator控件,可以對數據集進行增、刪、改、查詢等操作。在數據集的屬性控件中有一個cachedUpdate選項,將這一項設為true,delphi即允許以緩沖的形式來存取該數據集,也就是說對數據集所做的更新操作不會立刻自動反映到數據庫服務器,而只有調用實際提交的方法(如applyUpdates()等),delphi才將實際提交的數據反映到數據庫,同時通過數據集的on UpdateRecord()方法,來定義在實際更新數據庫表時需要同時進行的操作(如級聯刪除等)。這樣就為我們自己來控制數據提交的步驟提供了方便,雖然這使編程的難度有所增大,但是在某些場合我們需要這樣的靈活性。而且通過這種模式,確實大大減少了事務的長度、降低了網絡流量、增加了應用程序的可靠性。下面我們舉一個具體的應用模塊來說明如何運用這種編程模式。
五. Delphi程序舉例
(1) 應用背景說明
假設我們做一個商品定單處理的模塊。在這個模塊中涉及三個數據庫表:訂單表Order (有訂單號OrderID、金額SumMoney、日期date、客戶姓名costomer等字段),訂單明細表OrderDetail(有訂單明細號detailID、訂單號OrderID、商品編號CommondityID、數量amount、單價price等字段),庫存表storage(有商品編號CommondityID、現有庫存量stocks等字段)。其中,訂單與訂單明細表是一對多的關系,以訂單號OrderID作為連接字段。每當新增一份訂單的時候,都必須修改庫存表,將賣出的相應商品的數量從庫存中減去。
(2)程序框架說明
下面是一段delphi程序的框架,大致說明了如何運用緩存更新的編程模式。讀者可以自己將本程序的功能進一步的完善。
unit Order;
{單元名稱}
interface
uses
{引用的模塊}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, ExtCtrls, DBCtrls, ToolWin, ActnMan, ActnCtrls,
ActnMenus, DB, DBTables;
Type
{聲明的變量、添加的控件以及定義的方法和過程}
TOrderForm = class(TForm)
TBOrder: TTable;
TBDetail: TTable;
OrderDB: TDatabase;
ActionMainMenuBar1: TActionMainMenuBar;
DBNavigator1: TDBNavigator;
DBGrid1: TDBGrid;
procedure TBOrderAfterPost(DataSet: TDataSet);
procedure TBDetailNewRecord(DataSet: TDataSet);
procedure TBDetailUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
procedure TBDetailAfterPost(DataSet: TDataSet);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
OrderForm: TOrderForm;
implementation
{$R *.dfm}
{下面的內容為主要的程序框架}
procedure TOrderForm.FormCreate(Sender: TObject);
{將主表與明細表的緩存更新選項設為true}
begin
TBOrder.CachedUpdates:=true;
TBDetail.CachedUpdates:=true;
end;
procedure TOrderForm.TBOrderAfterPost(DataSet: TDataSet);
{在提交Order表的更新后,執行本過程內容,本過程實現對主表和明細表的實際提交的事務。
注意:如果一個數據集的cachedUpdates屬性為true,
那么post這個動作僅僅是在客戶端緩沖區中進行一個提交動作,
而不是真正的提交給實際的數據庫。要實現真正的提交,
需要用到applyUpdates語句。}
begin
OrderDB.StartTransaction;//更新事務開始執行
try
TBOrder.ApplyUpdates;//對主表進行實際的更新
TBDetail.ApplyUpdates;//對明細表進行實際的更新
except
Orderdb.Rollback;//如果發生意外,那么回滾這個事務,退出該過程
exit;
end;
OrderDB.Commit;//如果沒有發生意外,那么完成事務提交
TBOrder.commitUpdates;//清空TBOrder表的客戶緩沖區
TBDetail.commitUpdates;// 清空TBDetail表的客戶緩沖區
end;
procedure TOrderForm.TBDetailNewRecord(DataSet: TDataSet);
{當新增一個明細表記錄時所完成的動作。}
begin
TBDetail.FieldByName('OrderID').AsInteger:=TBOrder.FieldByName('OrderID').AsInteger;
file://將主表的orderID字段賦給明細表的orderID字段,這個字段是兩個表的關聯字段
end;
procedure TOrderForm.TBDetailUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
{當實際更新數據庫表時,需要同時進行的操作在onUpdateRecord事件中定義,
在本例當中是進行明細表和庫存表的級聯更新操作。
注意:在本過程當中所進行的操作是在實際更新數據庫時所進行的動作,
而不是更新客戶端的緩存數據時所進行的動作}
Var temp_query:TQuery;
begin
if UpdateKind=ukInsert then file://如果更新類型是插入一個新的記錄,那么更新相應的庫存量
with temp_query do
begin
close;
SQL.clear;
SQL.add('update storage set stocks=stocks-:amount');
SQL.add(' where commondityID=:commondityID');
paramByName('amount'):=TBOrder.FieldByName('amount').AsFloat;
ParamByName('commondityID'):=TBDetai.FieldByName('commondityID').AsInteger;
execSQL; file://執行更新庫存的sql語句,將相應的庫存量減去。
end;
end;
procedure TOrderForm.TBDetailAfterPost(DataSet: TDataSet);
{當對明細表的記錄進行修改,并提交(post)之后,執行本過程中的語句。
注意:這種提交是針對客戶端數據的,并沒有真正反映到數據庫中去。
在本例當中,實現的功能是計算主表的總金額字段}
begin
TBOrder.FieldByName('money'):=0;
with TBDetail do
begin
first;
while not eof do
begin
TBOrder.FieldByName('money'):=TBOrder.FieldByName('money')+
FieldByName('price').AsFloat*FieldByName('amount');
file://將明細表的金額累加到主表的金額字段
next;
end;
end;
end;
end.