|
|
|
多层数据库开发十三:剖析几个数据库应用程序 |
热 ★★★★ |
|
多层数据库开发十三:剖析几个数据库应用程序 |
|
作者:闵涛 文章来源:闵涛的学习笔记 点击数:2383 更新时间:2009/4/23 18:30:21 |
|
第十三章 剖析几个数据库应用程序 前面已经详细讲述了Delphi 4的数据库编程技术。为了使读者能够透彻地理解有关编程技术并灵活运用,我们把Delphi 4的几个示范程序拿出来加以剖析,这些示范程序都编得非常有技巧。要说明的是,剖析程序时我们可能会忽略掉一些与主题无关的细节。 13.1 一个后台查询的示范程序 这一节详细剖析一个后台查询的示范程序,项目名称叫Bkquery,它可以在C:\Program Files\Borland\Delphi4\Demos\Db\Bkquery目录中找到。它的主窗体如图13.1所示。 图13.1 Bkquery的主窗体 我们先从处理窗体的OnCreate事件的句柄开始,因为它是应用程序的起点。Procedure TAdhocForm. FormCreate(Sender: TObject); Procedure CreateInitialIni; Const VeryInefficientName = ''''IB: Very Inefficient Query''''; VeryInefficientQuery =''''select EMP_NO, Avg(Salary) as Salary\n''''+'''' from employee, employee, employee\n'''' +'''' group by EMP_NO''''; AmountDueName = ''''DB: Amount Due By Customer''''; AmountDueByCustomer =''''select Company, Sum(ItemsTotal) - Sum(AmountPaid) as AmountDue\n'''' +'''' from customer, orders\n'''' +'''' where Customer.CustNo = Orders.CustNo\n'''' + '''' group by Company''''; Begin With SavedQueries Do Begin WriteString(VeryInefficientName, ''''Query'''', VeryInefficientQuery); WriteString(VeryInefficientName, ''''Alias'''', ''''IBLOCAL''''); WriteString(VeryInefficientName, ''''Name'''', ''''SYSDBA''''); SavedQueryCombo.Items.Add(VeryInefficientName); WriteString(AmountDueName, ''''Query'''', AmountDueByCustomer); WriteString(AmountDueName, ''''Alias'''', ''''DBDEMOS''''); WriteString(AmountDueName, ''''Name'''', ''''''''); SavedQueryCombo.Items.Add(AmountDueName); End; End; Begin Session.GetAliasNames(AliasCombo.Items); SavedQueries := TIniFile.Create(''''BKQUERY.INI''''); SavedQueries.ReadSections(SavedQueryCombo.Items); If SavedQueryCombo.Items.Count <= 0 then CreateInitialIni; SavedQueryCombo.ItemIndex := 0; QueryName := SavedQueryCombo.Items[0]; Unmodify;ReadQuery; End; FormCreate主要做了这么几件事情:首先,它调用TSession的GetAliasNames函数把所有已定义的BDE别名放到一个字符串列表中,实际上就是填充图13.1中的“Database Alias”框。接着,创建了一个TIniFile类型的对象实例,并指定文件名是BKQUERY.INI。如果这个文件现在还不存在的话,就需要调用CreateInitialIni去创建一个文件。至于怎样写.INI文件,这不是本章要讨论的主题。最后,调用ReadQuery把文件中保存的有关参数读出来。 ReadQuery函数是这样定义的: Procedure TAdhocForm.ReadQuery; Begin If not CheckModified then Exit; With SavedQueries Do Begin QueryName := SavedQueryCombo.Items[SavedQueryCombo.ItemIndex]; QueryEdit.Text := IniStrToStr(ReadString(QueryName, ''''Query'''', '''''''')); AliasCombo.Text := ReadString(QueryName, ''''Alias'''', ''''''''); NameEdit.Text := ReadString(QueryName, ''''Name'''', ''''''''); End; Unmodify; If Showing thenIf NameEdit.Text <> '''''''' then PasswordEdit.SetFocus else QueryEdit.SetFocus; End; 当用户单击“Execute”按钮,程序就调用BackgroundQuery在后台执行查询。Procedure TAdhocForm.ExecuteBtnClick(Sender: TObject); Begin BackgroundQuery(QueryName, AliasCombo.Text, NameEdit.Text, PasswordEdit.Text,QueryEdit.Text); BringToFront; End; BackgroundQuery是在另一个叫ResItFrm的单元中定义的,后面将重点介绍这个过程。当用户单击“New”按钮,程序就把窗体上的一些窗口重新初始化。 Procedure TAdhocForm.NewBtnClick(Sender: TObject); Function UniqueName: string; var I: Integer; Begin I := 1; Repeat Result := Format(''''Query%d'''', [I]); Until SavedQueryCombo.Items.IndexOf(Result) < 0; End; Begin AliasCombo.Text := ''''DBDEMOS''''; NameEdit.Text := ''''''''; PasswordEdit.Text := ''''''''; QueryEdit.Text := '''''''';QueryEdit.SetFocus; QueryName := UniqueName; SavedQueryCombo.ItemIndex := -1; Unnamed := True; End; 当用户单击“Save”按钮,程序就调用SaveQuery函数把当前有关参数保存到.INI文件中。 Procedure TAdhocForm.SaveBtnClick(Sender: TObject); Begin SaveQuery; End; 而SaveQuery是这样定义的: Procedure TAdhocForm.SaveQuery; Begin If Unnamed then SaveQueryAs Else With SavedQueries Do Begin WriteString(QueryName, ''''Query'''', StrToIniStr(QueryEdit.Text)); WriteString(QueryName, ''''Alias'''', AliasCombo.Text); WriteString(QueryName, ''''Name'''', NameEdit.Text);Unmodify; End; End; 当用户单击“Save As”按钮,程序调用SaveQueryAs函数以另一个名称保存有关参数。 Procedure TAdhocForm.SaveAsBtnClick(Sender: TObject); Begin SaveQueryAs; End; 而SaveQueryAs是这样定义的: Procedure TAdhocForm.SaveQueryAs; Begin If GetNewName(QueryName) then Begin Unnamed := False; SaveQuery; With SavedQueryCombo, Items Do Begin If IndexOf(QueryName) < 0 then Add(QueryName); ItemIndex := IndexOf(QueryName); End; End; End; 其中,GetNewName是在一个叫SaveQAs的单元中定义的,它将打开如图13.2所示的对话框,让用户输入一个文件名。图13.2 指定另一个文件名此外,程序还处理了SavedQueryCombo框的OnChange事件: Procedure TAdhocForm.SavedQueryComboChange(Sender: TObject); Begin ReadQuery; End; 所谓后台查询,实际上是运用多线程的编程技术,使查询在一个专门的线程中进行。为此,首先要以TThread为基类声明一个线程对象: TypeTQueryThread = Class(TThread)PrivateQueryForm: TQueryForm; MessageText: string; Procedure ConnectQuery; Procedure DisplayMessage; ProtectedProcedure Execute; override; PublicConstructor Create(AQueryForm: TQueryForm); End; 我们先看线程对象是怎样创建的: Constructor TQueryThread.Create(AQueryForm: TQueryForm); Begin QueryForm := AQueryForm; FreeOnTerminate := True; Inherited Create(False); End; 当用户单击“Execute”按钮,程序就调用BackgroundQuery函数在后台执行查询。BackgroundQuery是这样定义的: Procedure BackgroundQuery(const QueryName, Alias, User, Password, QueryText: string); var QueryForm: TQueryForm; Begin QueryForm := TQueryForm.Create(Application); With QueryForm, Database Do Begin Caption := QueryName; QueryLabel.Caption := QueryText; Show; AliasName := Alias; Params.Values[''''USER''''] := User; Params.Values[''''PASSWORD''''] := Password; Query.Sql.Text := QueryText; End; TQueryThread.Create(QueryForm); End; BackgroundQuery主要做了三件事情,一是动态创建和显示一个窗体(TQueryForm),因为要用这个窗体显示查询结果。二是把传递过来的参数分别赋给TDadabase构件的AliasName、Params以及TQuery构件的SQL属性。三是创建线程对象的实例。由于线程对象的FreeOnTerminate属性设为True,所以用不着专门去删除线程对象。 好,现在让我们看看这个程序最关键的代码,即线程对象的Execute函数: Procedure TQueryThread.Execute; varUniqueNumber: Integer; Begin Try With QueryForm Do Begin UniqueNumber := GetUniqueNumber; Session.SessionName := Format(''''%s%x'''', [Session.Name, UniqueNumber]); Database.SessionName := Session.SessionName; Database.DatabaseName:=Format(''''%s%x'''',[Database.Name,UniqueNumber]); Query.SessionName := Database.SessionName; Query.DatabaseName := Database.DatabaseName; Query.Open; Synchronize(ConnectQuery);MessageText := ''''Query openned''''; Synchronize(DisplayMessage); End; Except On E: Exception Do Begin MessageText := Format(''''%s: %s.'''', [E.ClassName, E.Message]); Synchronize(DisplayMessage); End; End; End; 由于这是个多线程的数据库应用程序,因此,需要显式地使用TSession构件,而且要保证每个线程所使用的BDE会话期对象是唯一的。所以,程序首先调用GetUniqueNumber来获得一个唯一的序号。同样,对于TDatabase构件来说,也有类似的问题。 Execute通过Synchronize让主线程去执行ConnectQuery、DisplayMessage等方法,这是因为ConnectQuery、DisplayMessage都需要与VCL打交道,必须用Synchronize作外套。 13.2 一个缓存更新的示范程序 这一节详细剖析一个缓存更新的示范程序,项目名称叫Cache,它可以在C:\Program Files\Borland\Delphi4\Demos\Db\Cacheup目录中找到。它的主窗体如图13.3所示。 图13.3 Cache的主窗体 主窗体上有一个“Cached Updates”复选框,如果选中此复选框,表示使用缓存更新技术。否则,表示不使用缓存更新技术,当用户修改了数据后,数据被直接写到数据集中。 主窗体上还有一个“Use Update SQL”复选框,如果选中这个复选框,表示使用TUpdateSQL构件来进行缓存更新。 当用户单击“Apply Updates”按钮,就向数据库申请更新数据。 当用户单击“Cancel Updates”按钮,所有未决的修改将被取消。 当用户单击“Revert Record”按钮,对当前记录所作的修改将被取消。 在“Show Records”分组框内有几个复选框,用于选择要在栅格中显示哪些记录,包括未修改的记录、修改的记录、插入的记录和删除的记录。 当用户单击“Re-Execute Query”按钮,就重新执行查询。此外,这个示范程序还用一个计算字段来表达当前的更新状态。 下面我们就来看看怎样实现上述功能。在介绍程序代码之前,我们先要介绍数据模块CacheData,因为几个关键的构件都是放在这个数据模块上,如图13.4所示。 图13.4 数据模块 数据模块上有四个构件,分别是:一个TDataSource构件,其名为CacheDS,一个TDatabase构件名为CacheDB,一个TQuery构件名为CacheQuery,一个TUpdateSQL构件名为UpdateSQL。 TQuery构件的OnCalcFields事件是这样处理的: Procedure TCacheData.CacheQueryCalcFields(DataSet: TDataSet); ConstUpdateStatusStr: array[TUpdateStatus] of string = (''''Unmodified'''', ''''Modified'''',''''Inserted'''', ''''Deleted''''); Begin If CacheQuery.CachedUpdates then CacheQueryUpdateStatus.Value := UpdateStatusStr[CacheQuery.UpdateStatus]; End; 上述代码用于给计算字段CacheQueryUpdateStatus赋值,以显示当前的更新状态。TQuery构件的OnUpdateError事件是这样处理的: Procedure TCacheData.UpdateErrorHandler(DataSet: TDataSet; E: EDatabaseError; UpdateKind:TUpdateKind; var UpdateAction: TUpdateAction); Begin UpdateAction := UpdateErrorForm.HandleError(DataSet, E, UpdateKind); End; 现在我们回到主窗体,从处理主窗体的OnCreate事件的句柄开始。 Procedure TCacheDemoForm. FormCreate(Sender: TObject); Begin FDataSet := CacheData.CacheDS.DataSet as TDBDataSet; FDataSet.CachedUpdates := CachedUpdates.Checked; SetControlStates(FDataSet.CachedUpdates); FDataSet.Open; End; 第一行代码从TDataSource构件的DataSet属性取出当前的数据集,第二行代码是根据复选框CachedUpdates来决定数据集的CachedUpdates属性,进而再调用SetControlStates函数设置窗体上有关控件的状态,最后调用Open执行查询。SetControlStates是这样定义的: Procedure TCacheDemoForm.SetControlStates(Enabled: Boolean); Begin ApplyUpdatesBtn.Enabled := True; CancelUpdatesBtn.Enabled := True; RevertRecordBtn.Enabled := True; UnmodifiedCB.Enabled := True; ModifiedCB.Enabled := True; InsertedCB.Enabled := True; DeletedCB.Enabled := True; UseUpdateSQL.Enabled := True; End; 下面是处理一些控件的事件。首先是复选框CachedUpdates的OnClick事件: Procedure TCacheDemoForm.To[1] [2] [3] [4] 下一页 没有相关教程
|
|
教程录入:mintao 责任编辑:mintao |
|
|
上一篇教程: 多层数据库开发十二:使用数据控件 下一篇教程: 多层数据库开发十四:剖析几个MIDAS示范程序 |
|
|
【字体:小 大】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口】 |
|
注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网] |
网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!) |
|
|
|
|
|
|
|
同类栏目 |
|
|
赞助链接 |
|
|
500 - 内部服务器错误。
|
|
|
|
|
|