查看文章 |
多层数据库开发(十三):剖析几个数据库应用程序
2008-09-03 17:39
第十三章 剖析几个数据库应用程序 前面已经详细讲述了Delphi 4的数据库编程技术。为了使读者能够透彻地理解有关编程技术并灵活运用,我们把Delphi 4的几个示范程序拿出来加以剖析,这些示范程序都编得非常有技巧。要说明的是,剖析程序时我们可能会忽略掉一些与主题无关的细节。 13.1 一个后台查询的示范程序 这一节详细剖析一个后台查询的示范程序,项目名称叫Bkquery,它可以在C:Program FilesBorlandDelphi4DemosDbBkquery目录中找到。它的主窗体如图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 '+' from employee, employee, employee ' +' group by EMP_NO'; AmountDueName = 'DB: Amount Due By Customer'; AmountDueByCustomer ='select Company, Sum(ItemsTotal) - Sum(AmountPaid) as AmountDue ' +' from customer, orders ' +' where Customer.CustNo = Orders.CustNo ' + ' 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 FilesBorlandDelphi4DemosDbCacheup目录中找到。它 |
最近读者: