Delphi中帶緩存的數(shù)據(jù)更新技術(shù)
發(fā)表時(shí)間:2024-02-21 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]一. 概念 在網(wǎng)絡(luò)環(huán)境下,數(shù)據(jù)庫應(yīng)用程序是c/s或者是多層結(jié)構(gòu)的模式。在這種環(huán)境下,數(shù)據(jù)庫應(yīng)用程序的開發(fā)應(yīng)當(dāng)盡可能考慮減少網(wǎng)絡(luò)數(shù)據(jù)傳輸量,并且盡量提高并發(fā)度;谶@個(gè)目的,帶緩存的數(shù)據(jù)更新技術(shù)應(yīng)運(yùn)而生,其大致過程就是:應(yīng)用程序?qū)?shù)據(jù)庫中數(shù)據(jù)提取到客戶端的緩沖區(qū),在緩沖區(qū)中完成數(shù)據(jù)的修改、更新、以...
一. 概念
在網(wǎng)絡(luò)環(huán)境下,數(shù)據(jù)庫應(yīng)用程序是c/s或者是多層結(jié)構(gòu)的模式。在這種環(huán)境下,數(shù)據(jù)庫應(yīng)用程序的開發(fā)應(yīng)當(dāng)盡可能考慮減少網(wǎng)絡(luò)數(shù)據(jù)傳輸量,并且盡量提高并發(fā)度。基于這個(gè)目的,帶緩存的數(shù)據(jù)更新技術(shù)應(yīng)運(yùn)而生,其大致過程就是:應(yīng)用程序?qū)?shù)據(jù)庫中數(shù)據(jù)提取到客戶端的緩沖區(qū),在緩沖區(qū)中完成數(shù)據(jù)的修改、更新、以及新數(shù)據(jù)的插入等操作;等操作完成之后,在一個(gè)合適的時(shí)間,一次性的將數(shù)據(jù)提交給數(shù)據(jù)庫,從而大大減少了網(wǎng)絡(luò)流量,減小了數(shù)據(jù)庫服務(wù)器的負(fù)載,提高了并發(fā)性能。
應(yīng)當(dāng)說,這并不是太新的技術(shù),在delphi等數(shù)據(jù)庫前端開發(fā)工具較早期版本中,就已經(jīng)對這種技術(shù)做出了的支持。但是筆者發(fā)現(xiàn)一些程序員并不注意這種技術(shù)的合理運(yùn)用,依然停留在單機(jī)應(yīng)用程序的思路下,導(dǎo)致所編出來的程序效率低下或者出現(xiàn)潛在的錯(cuò)誤。因此有必要對該技術(shù)的優(yōu)點(diǎn)、運(yùn)用原則以及運(yùn)用方法(以delphi為例)做一個(gè)總結(jié)。
二. 優(yōu)缺點(diǎn)
帶緩存的數(shù)據(jù)更新技術(shù)主要有如下幾個(gè)優(yōu)點(diǎn):
。1) 最大限度的減少了網(wǎng)絡(luò)流量,減少了數(shù)據(jù)存取的時(shí)間。提高了客戶端數(shù)據(jù)庫操作人員的效率。
。2) 減輕了數(shù)據(jù)庫服務(wù)器的負(fù)擔(dān),因?yàn)樵S多反復(fù)的更新、修改、刪除操作可以在客戶端的緩沖區(qū)內(nèi)完成,最后只把結(jié)果提交給服務(wù)器。
。3) 有效縮減事務(wù)(transaction)處理的時(shí)間,減少了并發(fā)事務(wù)的吞吐量。從而更能夠保證數(shù)據(jù)庫的一致性。
我們可以舉個(gè)例子來說明其優(yōu)越性:數(shù)據(jù)庫操作員往數(shù)據(jù)庫中插入一條數(shù)據(jù)記錄,但馬上發(fā)現(xiàn)該記錄不合要求,于是將該數(shù)據(jù)刪除。這一過程,如果采用不帶緩沖的數(shù)據(jù)更新技術(shù),則服務(wù)器端將執(zhí)行一次插入操作,一次刪除操作,在客戶端和服務(wù)器端進(jìn)行往返兩次的數(shù)據(jù)傳輸,而且如果此數(shù)據(jù)與其他的數(shù)據(jù)庫表有級聯(lián)關(guān)系,還必須考慮到數(shù)據(jù)的級聯(lián)更新、刪除、恢復(fù)等操作。如果采用帶緩存的數(shù)據(jù)更新方法,則可以在客戶端的數(shù)據(jù)緩沖區(qū)完成這兩步互逆的操作,而不會(huì)給服務(wù)器端帶來任何動(dòng)作,不會(huì)產(chǎn)生任何網(wǎng)絡(luò)數(shù)據(jù)傳輸以及數(shù)據(jù)的級聯(lián)更新操作。由此可見帶緩沖的數(shù)據(jù)更新技術(shù)的巨大優(yōu)勢。
帶緩存的數(shù)據(jù)更新有一個(gè)缺點(diǎn)就是因?yàn)閿?shù)據(jù)是在客戶端存放的,所以該數(shù)據(jù)如果被其它用戶更改的話,會(huì)產(chǎn)生丟失修改等不一致狀況,需要考慮其并發(fā)控制的細(xì)節(jié),充分考慮那些多變的數(shù)據(jù)可能出現(xiàn)的不一致的情況。這種技術(shù)的運(yùn)用需要一定的技巧與一定的思維轉(zhuǎn)變。
三. 運(yùn)用原則
任何技術(shù)的優(yōu)越性都是在一定的環(huán)境中體現(xiàn)出來,帶緩沖的數(shù)據(jù)庫更新技術(shù)主要在以下幾種情況下運(yùn)用更有優(yōu)勢:
。1) c/s或者多層的數(shù)據(jù)庫應(yīng)用的場合。這種情況下,可以有效降低網(wǎng)絡(luò)流量。在單機(jī)情況下,該技術(shù)沒有意義。
。2) 在多表的數(shù)據(jù)更新場合。例如主表/明細(xì)表(master/detail)結(jié)構(gòu)的更新中,往往兩個(gè)表的增刪是相互影響的,那么在客戶端完成兩個(gè)表的所有更新之后,最后可以定義一個(gè)事務(wù)來提交所做的更新操作。這樣便有效的縮短事務(wù)的時(shí)間,更好的保證了數(shù)據(jù)的一致性。
(3) 服務(wù)器負(fù)載能力有限的場合。如今,隨著pc機(jī)的速度提高以及價(jià)格的降低,客戶機(jī)和服務(wù)器的能力差別越來越小,服務(wù)器的服務(wù)能力相對地下降了?陀^上要求從軟件的角度來降低服務(wù)器的負(fù)擔(dān),而帶緩沖的數(shù)據(jù)更新則通過客戶端分擔(dān)一部分更新任務(wù)的方式,降低了服務(wù)器的負(fù)擔(dān)。
。4) 數(shù)據(jù)被同時(shí)更新的概率比較低的場合。如果數(shù)據(jù)庫中同一條數(shù)據(jù)很可能在同一時(shí)段被多個(gè)用戶更新,那么這種情況就不適合帶緩存的更新,因?yàn)樵谶@種情況下,容易產(chǎn)生數(shù)據(jù)的錯(cuò)誤覆蓋,從而導(dǎo)致數(shù)據(jù)的不一致。
四. Delphi中的控制方法概述
Delphi作為一個(gè)流行的數(shù)據(jù)庫開發(fā)工具,具有豐富的數(shù)據(jù)庫操縱功能。在帶緩沖的數(shù)據(jù)存取技術(shù)上,delphi做出了很全面的支持。通常,delphi為用戶提供了TTable和TQuery等幾種存取數(shù)據(jù)庫表的數(shù)據(jù)集控件;提供了TDBNevigator控件,可以對數(shù)據(jù)集進(jìn)行增、刪、改、查詢等操作。在數(shù)據(jù)集的屬性控件中有一個(gè)cachedUpdate選項(xiàng),將這一項(xiàng)設(shè)為true,delphi即允許以緩沖的形式來存取該數(shù)據(jù)集,也就是說對數(shù)據(jù)集所做的更新操作不會(huì)立刻自動(dòng)反映到數(shù)據(jù)庫服務(wù)器,而只有調(diào)用實(shí)際提交的方法(如applyUpdates()等),delphi才將實(shí)際提交的數(shù)據(jù)反映到數(shù)據(jù)庫,同時(shí)通過數(shù)據(jù)集的on UpdateRecord()方法,來定義在實(shí)際更新數(shù)據(jù)庫表時(shí)需要同時(shí)進(jìn)行的操作(如級聯(lián)刪除等)。這樣就為我們自己來控制數(shù)據(jù)提交的步驟提供了方便,雖然這使編程的難度有所增大,但是在某些場合我們需要這樣的靈活性。而且通過這種模式,確實(shí)大大減少了事務(wù)的長度、降低了網(wǎng)絡(luò)流量、增加了應(yīng)用程序的可靠性。下面我們舉一個(gè)具體的應(yīng)用模塊來說明如何運(yùn)用這種編程模式。
五. Delphi程序舉例
(1) 應(yīng)用背景說明
假設(shè)我們做一個(gè)商品定單處理的模塊。在這個(gè)模塊中涉及三個(gè)數(shù)據(jù)庫表:訂單表Order (有訂單號OrderID、金額SumMoney、日期date、客戶姓名costomer等字段),訂單明細(xì)表OrderDetail(有訂單明細(xì)號detailID、訂單號OrderID、商品編號CommondityID、數(shù)量amount、單價(jià)price等字段),庫存表storage(有商品編號CommondityID、現(xiàn)有庫存量stocks等字段)。其中,訂單與訂單明細(xì)表是一對多的關(guān)系,以訂單號OrderID作為連接字段。每當(dāng)新增一份訂單的時(shí)候,都必須修改庫存表,將賣出的相應(yīng)商品的數(shù)量從庫存中減去。
。2)程序框架說明
下面是一段delphi程序的框架,大致說明了如何運(yùn)用緩存更新的編程模式。讀者可以自己將本程序的功能進(jìn)一步的完善。
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}
{下面的內(nèi)容為主要的程序框架}
procedure TOrderForm.FormCreate(Sender: TObject);
{將主表與明細(xì)表的緩存更新選項(xiàng)設(shè)為true}
begin
TBOrder.CachedUpdates:=true;
TBDetail.CachedUpdates:=true;
end;
procedure TOrderForm.TBOrderAfterPost(DataSet: TDataSet);
{在提交Order表的更新后,執(zhí)行本過程內(nèi)容,本過程實(shí)現(xiàn)對主表和明細(xì)表的實(shí)際提交的事務(wù)。
注意:如果一個(gè)數(shù)據(jù)集的cachedUpdates屬性為true,
那么post這個(gè)動(dòng)作僅僅是在客戶端緩沖區(qū)中進(jìn)行一個(gè)提交動(dòng)作,
而不是真正的提交給實(shí)際的數(shù)據(jù)庫。要實(shí)現(xiàn)真正的提交,
需要用到applyUpdates語句。}
begin
OrderDB.StartTransaction;//更新事務(wù)開始執(zhí)行
try
TBOrder.ApplyUpdates;//對主表進(jìn)行實(shí)際的更新
TBDetail.ApplyUpdates;//對明細(xì)表進(jìn)行實(shí)際的更新
except
Orderdb.Rollback;//如果發(fā)生意外,那么回滾這個(gè)事務(wù),退出該過程
exit;
end;
OrderDB.Commit;//如果沒有發(fā)生意外,那么完成事務(wù)提交
TBOrder.commitUpdates;//清空TBOrder表的客戶緩沖區(qū)
TBDetail.commitUpdates;// 清空TBDetail表的客戶緩沖區(qū)
end;
procedure TOrderForm.TBDetailNewRecord(DataSet: TDataSet);
{當(dāng)新增一個(gè)明細(xì)表記錄時(shí)所完成的動(dòng)作。}
begin
TBDetail.FieldByName('OrderID').AsInteger:=TBOrder.FieldByName('OrderID').AsInteger;
file://將主表的orderID字段賦給明細(xì)表的orderID字段,這個(gè)字段是兩個(gè)表的關(guān)聯(lián)字段
end;
procedure TOrderForm.TBDetailUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
{當(dāng)實(shí)際更新數(shù)據(jù)庫表時(shí),需要同時(shí)進(jìn)行的操作在onUpdateRecord事件中定義,
在本例當(dāng)中是進(jìn)行明細(xì)表和庫存表的級聯(lián)更新操作。
注意:在本過程當(dāng)中所進(jìn)行的操作是在實(shí)際更新數(shù)據(jù)庫時(shí)所進(jìn)行的動(dòng)作,
而不是更新客戶端的緩存數(shù)據(jù)時(shí)所進(jìn)行的動(dòng)作}
Var temp_query:TQuery;
begin
if UpdateKind=ukInsert then file://如果更新類型是插入一個(gè)新的記錄,那么更新相應(yīng)的庫存量
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://執(zhí)行更新庫存的sql語句,將相應(yīng)的庫存量減去。
end;
end;
procedure TOrderForm.TBDetailAfterPost(DataSet: TDataSet);
{當(dāng)對明細(xì)表的記錄進(jìn)行修改,并提交(post)之后,執(zhí)行本過程中的語句。
注意:這種提交是針對客戶端數(shù)據(jù)的,并沒有真正反映到數(shù)據(jù)庫中去。
在本例當(dāng)中,實(shí)現(xiàn)的功能是計(jì)算主表的總金額字段}
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://將明細(xì)表的金額累加到主表的金額字段
next;
end;
end;
end;
end.