文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>delphi(12)

delphi(12)

时间:2006-06-10  来源:许我一个信仰

第13章  使用Data Access组件

不管是Delphi的专业版或者是企业版,都带有两个组件的属性页,用于辅助创建数据库应用程序。Data Access的属性页包含了连接组件,为连接到各种各样的数据源提供了方便。Data Controls的属性页包含了几个可视的控件,用于创建数据感知的图形用户接口。专业版和企业版的Delphi包含了几个附加的控件,使您可以使用ADO(ActiveX数据对象)、Interbase特定组件(用于协同Inprise的数据库服务器Interbase一起工作)、MIDAS组件(用于分布式客户端/服务器开发)、Decision Cube组件和DBExpress(上面的每一组组件将在本章的后面作介绍)。

注意:第13章经常使用Word组件和控件。一个控件是一个组件,但是一个组件不是一个控件。组件和控件的不同之处是控件有一个WndProc、一个Windows处理程序,并且控件有可视的方面。而组件可以有一个或者更多的WndProc和Windows处理程序,比如说TApplication是一个组件,该组件有一个Windows处理程序和一个WndProc,但是它没有任何可视的方面。

基本的数据控件使创建数据感知窗体变得扑朔迷离,从而影响到桌面或者企业级的数据库应用程序的开发。目前超过两门的学派研究数据库应用程序怎样被创建。其中有两个截然不同的学派是:第一个学派是使用两层的RAD(快速应用程序开发)方法的开发者;第二个学派认为RAD不好,他们倾向于多层的方法。两层应用程序一般指的是一种这样的数据库应用程序:其图形用户接口直接连接到数据库。通常,直接在窗体上放置数据控件和data access组件就可以创建两层的应用程序。控件连接到数据库的右端。通常三层应用程序意味着至少有一层将数据库和图形用户接口分离开来。

在三层应用程序中,窗体通常包含一个限定数量的交互逻辑,其主要的功能是将数据传输给用户或者从用户接受数据。中间层通常包含交互控制对象、数据库连接对象和从图形用户接口接受数据或者向图形用户接口传输数据的对象。不管是两层的还是三层的应用程序都是开发应用程序的有效形式。

两层的应用程序接口通常是由数据的需要来驱动的,它们被叫做数据库的合成物,使用起来非常简单,可能最适用于小应用程序和桌面应用程序。两层应用程序最适合实用应用程序,它只有一个预定目标平台(如Windows)并且其预算很少。虽然有可能设计并实现很出色的两层应用程序,但是它们仍然不大可能成为独立的平台。

三层应用程序通常被认为更强大、更具伸缩性,其实现要有更大的代价。首先,三层应用程序需要考虑更多的构思;如果这一步做得很差应用程序将失去伸缩性,可能运行起来会很慢并且创建起来会很昂贵。一个实现得很差的三层应用程序可能会和一个弱的两层应用程序一样差或者更差。

成功的关键是设计观念化。一个好的设计师和一支具有献身精神的程序员如果很好地协作将会产生最好的效果。因为数据控件直接支持两层形式的开发,数据控件的介绍更缺乏,所以本章将介绍在两层应用程序中使用data access组件和数据控件。记住这相对于其他形式来说并不是很好。一个更可取的方法是考虑预算、目标平台、应用程序复杂性、用户通信和将来难以预料的变化,这要专门雇佣一个设计师来设计(第15章介绍了怎样创建中间层产品和怎样使用MIDAS进行分布式计算)。

13.1  ODBC(开放式数据库连接)

ODBC是20世纪90年代流行起来的,它为应用程序创建一个同数据库连接的协议。它是一个API(应用程序接口)定义。每一个厂商都可以创建DLL来实现同等的并与ODBC API兼容的API过程;特殊厂商的API提供一种兼容的方法,通过此方法开发者可以用程序同这个厂商的数据库引擎通信。

比如说,Microsoft Access 和Oracle已经在1996年为它们各自动数据库实现了ODBC驱动程序,但是DB2没有。因此连接到一个Access或者Oracle数据库的方法非常简单,但是连接到DB2数据库需要使用用C编写的存储过程和来自IBM的专有连接。

ODBC背后的目标是将应用程序写到ODBC API中,开发者改变数据库引擎而不用修改代码。这个特性和灵活性仍然是使用ODBC的一个原因。

13.1.1  创建ODBC别名

Microsoft在20世纪90年代初(参看图13.1)将ODBC数据源管理器捆绑到Windows中。ODBC管理器保存了与ODBC驱动程序相关的名称、特定的数据库文件和特殊数据库引擎需要的任何其他信息。当在程序中用到被创建的名称的时候,相应的ODBC DLL和相应类型的数据库将被用到。

图13.1  ODBC数据源管理器(从控制面板的ODBC小应用程序中打开)

下面的例子使用Paradox表,您不必用Paradox的拷贝来创建Paradox数据库。

1.打开控制面板,双击Data Sources小应用程序运行ODBC数据源管理器(程序在c:\winnt\system32\odbcad32.exe,在这里c:\winnt是您安装Windows的目录)。

2.给您的用户配置文件(您登录后的Windows配置信息)创建ODBC别名,选择“User DSN”属性页。要给所有的用户创建别名,选择“System DSN”属性页(任何一个属性页都用于我们特定的目的,但是如果您将您的工作站同其他人共享的话,他们也可以使用您创建的别名)。

3.单击Add按钮开始创建别名(如图13.1所示),这一步将弹出一个Create New Data Source对话框向导。

4.Create New Data Source对话框是为Office 2000修订的,但是它将默认安装您已经安装的所有驱动程序,每一个驱动程序代表一个不同的数据库和版本。找到并选择Microsoft Paradox驱动程序。

5.单击Finish按钮。这一步将打开对应于您所选择的数据库的ODBC安装向导。简单的数据库引擎如Paradox将打开一个类似于如图13.2所示的对话框,对话框的形式要依赖于您所使用的ODBC管理器的版本,复杂的数据库引擎将打开一系列复杂的对话框,用于复杂的设置,比如说Microsoft 的SQL Server。

6.对于本例,在Data Source Name文本框中输入Test(如图13.2所示)。

图13.2  ODBC安装对话框,显示了桌面数据库引擎

7.取消Use Current Directory复选框,单击Select Directory按钮(如图13.2所示)。

8.在打开的对话框中导航到<Delphi>\Borland Shared\Data目录,在这个目录里已经安装了演示版的数据库。其中“<Delphi>”表示您所安装Delphi的位置。

9.单击OK按钮。

10.在ODBC数据源管理器中确认一个新的别名Test已经列在User Data Sources或者System Data Source的列表框中了,别名在哪个列表框中要依赖于您是在User DSN还是在System DSN属性页中添加数据源的。

11.单击OK按钮关闭ODBC管理器。

注意:Paradox表保存在分开的文件中,当您指向一个Paradox数据库的时候,实际上指向的是一个目录,表文件就存储在这个目录里。

现在当您指向Test别名的时候,Delphi会知道使用Microsoft的Paradox ODBC驱动程序,并知道表存储在<Delphi>\Borland Shared\Data目录中。

13.1.2  改变ODBC别名的配置

如果您的数据库被移动了或者您需要修改这个数据库(已经有一个别名指向这个数据库)的配置,或者您由于某种原因需要升级一个ODBC别名,那么使用控制面板中的ODBC Data Source小应用程序。运行ODBC小应用程序,找到您要修改的别名,单击Configure按钮。做一些适当的修改,然后单击OK按钮保存所做的修改。这为在测试数据库和产品数据库之间进行切换提供一种便利的方法,比如说,当开发一个应用程序的时候。

13.1.3  测试连接

使用SQL Explorer测试一个ODBC连接。SQL Explorer允许您查看一个ODBC连接和一个别名指向的表(它还有许多其他的特性,这将在本章后面介绍SQL Explorer和Monitor的时候作详细的讨论)。运行SQL Explorer的方法有:单击Delphi中的Database中的Explore菜单,或者从Delphi的程序组中运行,通过依次选择Start、Program Files、Borland Delphi和SQL Explorer运行此程序。SQL Explorer如图13.3所示。

图13.3  SQL Explorer界面(图中打开了前面创建的数据库别名所指向的一个数据库)

使用前面的任何一种方法,打开SQL Explorer。如图13.3所示,单击Databases属性页。Databases属性页列出了所有存储在SQL Explorer中的数据库别名。找到别名Test并单击别名名称旁边的符号“+”。当提示您输入密码的时候,单击OK按钮,这里不需要密码。单击Tables项旁边的符号“+”展开表的列表。单击“Biolife”表。在Explorer的右边将出现一个Data属性页。单击Data属性页,您将看到类似于图13.3所示的数据。

提示:您也可以用SQL Explorer作为一个SQL语句的测试平台。

 

您也可以在Enter SQL属性页中的编辑域中输入SQL(结构化查询语言,一种数据库编程语言)语句。单击发亮的图标运行查询(在Data Access组件介绍SQL的时候将介绍基本的SQL语言)。

13.2  Borland数据库引擎

BDE(Borland数据库引擎)是一个API,它为Inprise应用程序(包括Delphi)提供一个本地数据库支持。Borland数据库管理器提供BDE的配置管理。BDE管理器在Control Panels小应用程序中,它使您可以为被支持的数据库和ODBC别名指定一个别名,此别名使用本地BDE数据库驱动程序。当您在应用程序中包括BDE单元的时候,您可以使用本地C/C++类型的 API调用来直接管理数据库。

BDE是和Data Access组件完全分开的。您完全可以不用本地BDE数据库API调用来创建所有的应用程序。通常,在优化性能中所能获得的好处是可维护性和强大的功能。来自TDataSet的子类是TBDEDataSet类,它封装了BDE的功能。如果您想使用BDE,那么继承类TBDEDataSet或者TDBDataSet。通常BDEDataSet对象是不进行初始化的,但是提供的行为可以通过TTable, TQuery和TStoredProcedure访问(想了解更多的信息请参考Data Access组件,那里有BDE行为的介绍)。

13.3  数据库窗体向导

数据库窗体向导使您可以创建一个低级的数据库应用程序,此应用程序示范了一个最小的两层数据库应用程序,也是最本质的部分。虽然这个例子很普通,但是其实用性是很强的。

提示:软件的收缩性不是很好。使用数据库窗体向导创建的数据感知窗体当超过50个这样的窗体添加进来的时候,它就不能成为一个复杂的、面向对象系统的一部分了。软件的复杂性随着关系的数目和多样性成幂指数增长。可选地,创建三个或四个数据库窗体是一种创建低成本程序的方法,这种方法作用很大而不昂贵,也不会超过开发者的能力范围。

遵循下面的步骤创建一个单一表的数据库程序,根据您的爱好,用任何随数据库一起创建的表替换这个已经命名的表。这个示例表包含了Lansing Capitol City Renegades(一个小部门的曲棍球队)的比赛统计。

表PLAYER_STATISTICS是用SQL Server 2000设计器创建的。下面的脚本是从这个设计器导出的,并以.SQL的扩展名保存为文本文件(想更多的了解SQL请参考Data Access组件中的SQL部分)。

if exists (select * from dbo.sysobjects where id =

object_id(N'[dbo].[PLAYER_STATISTICS]') and OBJECTPROPERTY(id,

N'IsUserTable') = 1)

drop table [dbo].[PLAYER_STATISTICS]

GO

CREATE TABLE [dbo].[PLAYER_STATISTICS] (

[ID] [int] NOT NULL ,

[PLAYER_NAME] [varchar] (25) COLLATE

SQL_Latin1_General_CP1_CI_AS NULL ,

[NUMBER_OF_GAMES] [int] NULL ,

[GOALS] [int] NULL ,

[ASSISTS] [int] NULL ,

[POINTS] [int] NULL ,

[PENALTY_MINUTES] [int] NULL

) ON [PRIMARY]

GO

注意:通常,SQL命令和表名称用大写字母。上面所列的代码是ANSI-92 SQL和Microsoft的T-SQL(针对SQL Server 2000)语言的混合体。

简而言之,SQL Server 2000生成的脚本检查表PLAYER_STATISTICS是否存在。如果这个表已经存在,那么执行DROP TABLE命令。最后运行CREATE TABLE命令生成这个表。

第一步创建一个ODBC别名,此别名指向本例中您所选择的数据库。从下一步开始,需要一个指向Renegades数据库的别名(以您的数据库名代替Renegades数据库别名)。数据库窗体向导忽略BDE别名,所以您需要获取一个BDE别名的表或者当您创建ODBC别名的时候创建一个BDE别名。可以把BDE想象为一个封装OBDC的层,用于便利Inprise的开发工具(包括Delphi)。下一部分介绍怎样创建一个BDE别名,之后介绍怎样使用窗体向导。

13.3.1  使用SQL Explorer创建BDE别名

您可以使用Delphi中的Database菜单中的Explore菜单创建BDE别名。如果您有Delphi的标准版本,这个菜单项将打开Database Explorer;如果是专业版或者企业版将打开SQL Explorer。这个两个程序都可以用来管理BDE别名(SQL Explorer如图13.4所示)。使用本部分开始的Renegades SQL Server数据库,遵循下面的步骤创建这个BDE别名(记住如果您正为其他数据库创建别名请替换您的数据库信息)。

1.参照图13.4,单击Object中的New菜单项,打开New Database Alias对话框。

2.在对话框中选择您要创建别名的数据库的驱动程序。在Renegades的例子中,选择SQL Server Database Driver,然后单击OK按钮(结果如图13.4所示)。

3.在右边的Definition属性页中(参照图13.4),找到ODBC DSN项,在其右边的单元格中输入Renegades(您的数据库别名)。

4.单击默认的名称ODBC1,然后单击Object中的Rename菜单项,将ODBC1重新命名为Renegades(您的别名名称)。

5.单击Object中的Apply菜单项应用所做的修改。

6.单击Object中的Open菜单项,验证可以使用刚才创建的别名打开数据库。

7.当您确保别名配置正确的情况下(您可以打开数据库),单击Object中的Close菜单项关闭数据库。

图13.4  Delphi专业版和企业版中的SQL Explorer用于管理BDE别名。标准版本

      的Delphi使用一个相似的程序——Database Explorer,其功能是一样的

ODBC和BDE别名都已经被配置了,下面将运行数据库窗体向导创建应用程序。

13.3.2  使用数据库窗体向导

开始本练习,请单击File中的New菜单项运行数据库窗体向导,或者您也可以在任何时候单击Database中的Form Wizard菜单项运行该向导。请遵循下面的步骤,用本节开始的时候定义的数据库创建一个新的应用程序。

1.运行Delphi。

2.依次单击File、New、Other打开New Items对话框。

3.单击Business属性页,然后双击Database Form Wizard项。将弹出Database Form Wizard对话框(如图13.5所示)。

图13.5  数据库窗体向导的第1步(可以从创建新应用程序过程中的New

          条目中或者任何时候从Database  Form Wizard菜单中运行该向导)

4.保留默认值并使用一个TTable组件创建一个简单的窗体。

5.单击Next按钮。

6.从驱动程序或者别名名称组合框中选择Renegades名称(请参照图13.6),然后单击dbo.PLAYER_STATISTICS表(记住现在替换您的数据库别名)。

图13.6  向导的第2步,选择驱动程序或者别名以及表名设置从哪个表创建窗体

7.单击Next按钮。

8.单击下一步的>>按钮(见图13.7)将所有列添加到Ordered Selected Fields列表框中(您可以使用上下箭头,当前显示为不可用,重新排序列)。

图13.7  使用第3步添加和排序显示在数据库窗体上的字段

9.单击Next按钮。

10.下一步,选择Vertically单选按钮(没有图示)创建一个窗体,在一个垂直的列中显示标签和字段。

11.单击Next按钮(接受默认值)。

12.单击Next按钮(接受下一个默认值)。

13.单击Finish按钮生成窗体(如图13.8所示)。

图13.8  从第1步到第13步生成的数据库窗体

按F9键运行包含窗体的应用程序。使用上面的步骤,数据库窗体向导创建了一个简单的数据库窗体应用程序,其中包含TField,一个 TDataSource,一个TTable,一个 TDBNavigator和几个TDBEdit 控件与TLabel(如图13.8所示)。下一节将更详细地介绍这些组件。在练习中所有这些步骤可以在大约10分钟的时间内完成,所以创建基本的实用应用程序也是相对较快的(请参考第11章使用一个组件在运行时自动创建一个数据库窗体的例子)。

13.4  Data Access组件

您总可以简化复杂的层次并将它们编写到框架中。购买一个BASM(Borland Assembler)的拷贝,您就可以开始创建一个框架。被添加代码的层用于隐藏与创建应用程序框架有关的复杂问题,这给您开发应用程序提供了更高的起点。

注意:一个比较好的编写软件的模拟是住宅建筑工业。住宅是很复杂的。经过几千年的建造住宅之后,建筑师已经折衷成几十个标准化单元。建筑者也使用建筑师、通常的承建人、子承建人、壁画工、油漆师、艺术建筑师、检查官、管工和电工。然而软件要远远复杂得多,仍然由一个特定的部门和程序员与工程管理员团队编写。除非您非常幸运,许多开发团队没有相应的建筑师、设计师、工具制造师、专家和保证质量的团体成员(请与[email protected]联系告诉我您的团体是怎样编写软件的)。

Data Access组件提供相对高级别的水准,方便了数据库的访问。TDataSet是由TComponent(它引入数据库数据作为一个类的概念)直接继承的。

13.5  TDataSet

TDataSet类引入了基本属性和方法,为连接到数据库以及管理记录和字段提供了方便。表13.1列出了属性,表13.2列出了方法,表13.3列出了TDataSet的事件属性,这些将被TTable和TQuery继承,这两个TDataSet的派生类在组件面板的Data Access属性页上作为组件被列出。

表13.1  TDataSet属性

属性

说明

Active

公有属性,打开或者关闭一个与数据库的连接;在打开连接之前将调用BeforeOpen事件方法,数据集(dataset)被设置为dsBrowse模式,打开一个数据库光标,然后调用AfterOpen事件方法

ActiveRecord

保护属性,该属性返回活动记录的索引

AggFields

公有属性,该属性返回一个聚合字段的集合;在其子组件,如TClient DataSet(TClient DataSet可以在企业版Dephi的MIDAS属性页上找到,它支持聚合字段)中使用

AutoCalcFields

如果设置为True,将调用OnCalcFields事件使您可以创建一个字段,该字段的值由几个字段值来确定(比如说,FULL_NAME := LAST_NAME + ',' + FIRST_NAME)

BlobFieldCount

保护属性;BLOB(二进制大对象)字段的数目

BlockReadSize

设置为0块,读模式被禁止;设置为一个大于0的值将快速扫描大量的记录并禁止数据控件的升级

BOF

开始测试文件;测试光标是否定位在数据集的第一个记录

Bookmark

在数据集中设定标记,用于在一个数据集中获得或者设置当前记录

BookmarkSize

保护属性;该属性表示用于给一个指定的数据集保存一个书签所需要的字节数

BufferCount

保护属性;高速缓存的记录数

Buffers

保护属性;一个PChar(指针字符,C类型字符串)数组,指向内部高速缓存的记录缓冲区

CalcBuffer

保护属性;计算值和查找值保存在CalcBuffer中

CalcFieldsSize

保护属性;用于保存计算字段的缓冲区大小

CanModify

决定数据集是否可写

Constraints

TCheckConstraints包含记录等级要求,这会在编辑一个记录的时候遇到

CurrentRecord

保护属性;在内部高速缓存的记录缓冲区中的当前记录的索引

DataSetField

指定一个主数据集中的一个字段

DataSource

另外一个数据集的数据源,为本数据集提供数据

(续表)

属性

说明

DefaultFields

如果其值为True,那么这个数据集使用动态分配的字段;否则数据集在设计时使用字段编辑器将字段组件添加进来

Designer

用于确定数据集设计器是否是激活的,如果不空则返回这个设计器(比如说字段编辑器就是一个设计器)的引用(请参考本章关于动态和静态字段对象部分)

EOF

记录缓冲区(即光标)指向数据集中的最后一个记录

FieldCount

与该数据集有关的字段组件的数目

FieldDefList

字段定义列表,提供字段定义的平面视图

FieldDefs

数据集中字段定义的分层列表(例如,可以用来在新表中定义字段,请参考表13.1之后的列表)

FieldList

数据集中字段组件的连续列表

FieldNoOfs

保护属性;用于将字段索引转变为字段编号的偏移量

Fields

数据集中字段的TField集合,用于访问动态字段;访问静态字段要通过设计时生成的字段组件

FieldValues

对于Fields集合的默认数组属性;由字段名索引

Filter

过滤记录;其行为如SQL语句中的WHERE子句

Filtered

Boolean属性,表示是否应用过滤

FilterOptions

TFilterOptions (foCaseInsensitive,foNoPartialCompare);忽略大小写的比较;无局部的比较,把星号(“*”)当作一个文字值——如果foNoPartialCompare不在选项中,那么星号(“*”)被当作一个掩码字符

Found

表示FindFirst, FindNext, FindLast,或者FindPrior是否成功

InternalCalcFields

受保护的Boolean属性,表示内部计算的字段是否包含在数据集中

Modified

表示数据集是否被更改了

Name

数据集组件名称

NestedDataSetClass

NestedDataSet类的类引用

NestedDataSets

所有NestedDataSet的一个TList

ObjectView

当为True时,数据集中的字段被分层保存。如果为False时,字段被平铺并且嵌套的字段以姐妹的关系进行保存

RecNo

数据集的记录号,该数据集不支持记录号;TDataSet中的默认值是-1

RecordCount

与数据集相关的记录总数

RecordSize

记录的数据大小

Reserved

保护指针,为内部使用保留的

SparseArrays

表示TField是否为数组的每一个元素创建一个TField。默认值是False(不创建),使用一个稀疏数组,且数组的每一个元素都不获得一个TField

State

由以下可能枚举值之一指定的TDataSetState,枚举值为dsInactive, dsBrowse, dsEdit,dsInsert, dsSetKey, dsCalcFields, dsFilter,dsNewValue, dsOldValue, dsCurValue, dsBlockRead,dsInternalCalc, dsOpening

下面的列表介绍TDataSet的一些属性,这些属性可以通过TTable组件访问。这个例子介绍了怎样使用FieldDefs属性动态地创建一个表(请参考表13.2所列的TDataSet的方法)。

procedure TForm1.Button1Click(Sender: TObject);

var

FieldDef : TFieldDef;

IndexDef : TIndexDef;

begin

Table1.DatabaseName := 'DBDEMOS';

Table1.TableType := ttParadox;

Table1.TableName := 'FieldDefs';

Table1.FieldDefs.Clear;

FieldDef := Table1.FieldDefs.AddFieldDef;

FieldDef.Name := 'Greetings';

FieldDef.DataType := ftString;

FieldDef.Size := 25;

Table1.CreateTable;

Table1.Open;

Table1.Insert;

Table1.FieldByName('Field').AsString := 'Hello World!';

Table1.Post;

Table1.Close;

end;

TDataSet方法由其子类组件的实例调用,包括TTable, TQuery和TStoredProcedure。

表13.2  TDataSet方法

方法

说明

ActiveBuffer

返回一个PChar,包含激活记录的数据

Append

添加一个新的记录到数据集中

AppendRecord

添加一个新的记录到数据集中。以数组参数传递来的值填充字段

BookmarkValid

该方法传递一个Bookmark参数,如果此Bookmark在数据集中有效则返回True

Cancel

取消对数据集的修改,并设置数据集的状态为dsBrowse

CheckBrowseMode

如果数据集已经被修改了,则发送修改,如果状态设置为dsEdit或者dsInsert以及Modified设置为False,则取消修改

ClearFields

清除激活记录的所有字段值

Close

关闭数据集

CompareBookmarks

比较两个书签,如果这两个书签引用同样的记录则返回0,如果第一个书签指定所引用的记录在数据集中的位置比第二个书签在数据集中的位置靠前则返回一个小于0的值,否则返回一个大于0的值

ControlsDisabled

Boolean特性,表示相应的控件是否失效

Create

构造函数

(续表)

方法

说明

CreateBlobStream

从一个Field参数创建一个BlobStream(请参考Delphi帮助中的TStream和TBlobStream以获得更详细的资料)

CursorPosChanged

使内部光标定位无效

Delete

删除当前的记录

Destroy

析构函数

DisableControls

在更新过程中使相应的控件无效

Edit

将记录的状态设置为dsEdit;记录在编辑模式下

EnableControls

使相应的控件有效

FieldByName

返回动态的TField,通过字段名搜索

FindField

如果找到指定的字段名则返回一个TField;否则返回nil

FindFirst

返回一个Boolean值,表示查找的成功或者失败;将光标定位在数据集中的第一个记录上

FindLast

返回一个Boolean值,表示查找的成功或者失败;将光标定位在数据集中的最后一个记录上

FindNext

返回一个Boolean值,表示查找的成功或者失败;将光标定位在数据集中当前记录的下一个记录上

FindPrior

返回一个Boolean值,表示查找的成功或者失败;将光标定位在数据集中当前记录的前一个记录上

First

这是一个过程,将光标定位在第一个记录上

FreeBookmark

该方法传递一个用GetBookmark方法返回的书签,释放这个书签

GetBlobFieldData

返回BLOB字段值,根据FieldNo将值返回到一个字节数组:TBlobFieldData

GetBookmark

返回代表当前记录的书签

GetCurrentRecord

返回一个Boolean值,表示Buffer参数是否被当前记录缓冲区的值所填充

GetDetailDataSets

用每一个嵌套的数据集填充TList参数

GetDetailLinkFields

用字段组件(此组件构成了一个主细节关系)填充两个TList参数

GetFieldData

这是一个重载函数,如果成功的话以字段数据填充一个缓冲区

GetFieldList

将所有由FieldNames句点(.)分隔参数指定的字段组件拷贝到TList参数中

GetFieldNames

返回数据集中所有字段名的一个列表,保存在TStrings参数中

GotoBookmark

将光标定位到由Bookmark参数指定的记录中

Insert

将数据集设置为插入模式(State = dsInsert)

InsertRecord

插入一个记录,字段值由传递过来的变体数的常量数组填充

IsEmpty

一个Boolean值,表示数据集是否为空

IsLinkedTo

如果数据集已经连接到参数TDataSource,则返回True

IsSequenced

如果数据库表格由数据集表示则返回True,表示记录号码是否代表记录的顺序

Last

将光标定位到数据集中的最后一个记录

 

(续表)

方法

说明

Locate

查找数据集中的关键字段,其值由参数(工具TLocateOptions参数)传递过来,如果找到记录则返回True

Lookup

如果找到记录则返回字段值

MoveBy

将光标定位到由当前记录加上偏移量所代表的记录上

Next

将光标定位到下一个记录

Open

打开数据集

Post

将记录中的修改发送到数据库

Prior

将光标定位到前一个记录

Refresh

重新从数据库读取数据

Resync

从数据库中重新获取前一个、当前的和下一个记录

SetFields

变体数组参数(这个数组中的值被传递到记录的字段中)设置字段值;状态必须设置为dsEdit或者dsInsert

Translate

拷贝源字符串到目标字符串,使字符串值在ANSI字符映射和BDE字符映射之间进行转换

UpdateCursorPos

内部使用,以确保该光标被定位在激活的记录上

UpdateRecord

用于更新数据感知控件和数据集,以反映记录的更改

UpdateStatus

高速缓存的记录更新状态(usUnmodified,usModified,usInserted,usDeleted)

表13.3  TDataSet的事件特性

事件特性

说明

AfterCancel

在Cancel方法之后调用该事件

AfterClose

在Close方法之后调用该事件

AfterDelete

在Delete方法之后调用该事件

AfterEdit

在数据集设置为编辑模式之后调用该事件

AfterInsert

当一个新记录插入到该数据集中时调用该事件

AfterOpen

打开数据集之后调用该事件

AfterPost

在Post方法之后调用该事件(比如说,当当前记录被修改了且激活的记录被更新的时候将调用该事件)

AfterRefresh

Refresh方法之后调用该事件

AfterScroll

当光标定位被改变的时候调用该事件

BeforeCancel

当调用Cancel方法的时候,在执行Cancel行为之前调用该事件过程

BeforeClose

当调用Close方法的时候,在执行Close行为之前调用该事件

BeforeDelete

当调用Delete方法的时候,在执行Delete行为之前调用该事件(请参考该表格之后的DB.pas文件的程序列表,该程序列出了Delete方法的实现)

BeforeEdit

当调用Edit方法的时候,在执行Edit行为之前调用该事件

BeforeInsert

当调用Insert方法的时候,在记录被插入到数据集之前调用该事件

(续表)

事件特性

说明

BeforeOpen

当调用open方法的时候,在数据集被打开之前调用该事件

BeforePost

当调用Post方法的时候,在记录被发送到数据集之前调用该事件

BeforeScroll

当调用Scroll的时候,在光标位置改变之前调用该事件

OnCalcFields

当Calculated字段需要指定一个值的时候调用该事件

OnDeleteError

当执行Delete操作的时候,如果发生了一个EDatabaseEngine异常,将由CheckOperation调用该事件

OnEditError

(参见OnDeleteError)

OnFilterRecord

编写这个事件处理程序来测试过滤条件对于每个记录的过滤过程

OnNewRecord

当一个新的记录添加到数据集中的时候调用该事件

OnPostError

(参见OnDeleteError)

你可以为给定的DataSet组件的任何特性、甚至所有事件特性指定事件方法,这使您的代码可以在这些事件被执行之前或者之后作出响应。请看下面的代码,该代码摘自DB.pas,说明了TDataSet怎样实现这一步。

procedure TDataSet.Delete;

begin

CheckActive;

if State in [dsInsert, dsSetKey] then Cancel else

begin

if FRecordCount = 0 then DatabaseError(SDataSetEmpty, Self);

DataEvent(deCheckBrowseMode, 0);

DoBeforeDelete;

DoBeforeScroll;

CheckOperation(InternalDelete, FOnDeleteError);

FreeFieldBuffers;

SetState(dsBrowse);

Resync([]);

DoAfterDelete;

DoAfterScroll;

end;

end;

DataSet组件的特性在设计时在其子孙组件中作了某些修改。在应用程序中通过子孙组件来调用DataSet的事件处理程序和方法(请参考下面有关TTable,TQuery和 TStoredProcedure的介绍,其中有怎样使用这些属性的例子)。

13.6  TBDEDataSet和TDBDataSet

TBDEDataSet是TDataSet的另外一个子孙类。TBDEDataSet将本地BDE API行为集成到了TDataSet行为中。TBDEDataSet具有缓高速缓存更新的能力,使您的应用程序可以通过应用许多记录的整批修改来减少网络阻塞。

TDBDataSet将数据库连接引入到TBDEDataSet。在本变革中对类进行分层(一个TDBDataSet是一个TBDEDataSet,一个TDataSet是一个TComponent)是为了引入附加的行为,而取代创建一个单一的、全包括的类。比如说,如果您想定义一个类来扩展BDE的功能,那么您可以继承TBDEDataSet,而不从TDBDataSet继承所有的条款。

在设计数据感知应用程序时用到的组件直接从TDBDataSet派生,包括上面叙述过的TTable, TQuery和 TStoredProcedure组件。因为我们从祖先类(TDBDataSet, TBDEDataSet, TDataSet,TComponent, TPersistent和TObject)中继承了所有的特性和方法,所以我们将重点放在这些组件上。

13.7  TTable组件

数据库窗体向导默认情况下在窗体上放置一个TTable, 一个TDataSource、一个TDBNavigator组件和几个其他的数据控件(见图13.8)。对一个基本的桌面数据库数据感知窗体,这是您全部所需要的。

注意:桌面数据库,不同于客户端/服务器数据库,类似于Paradox, dBase, Access和FoxPro这样的数据库。客户端/服务器数据库包括Interbase, Oracle, SQL Server, UDB和 Sybase。文本文件和Excel电子表格可以归到这一类:由于Delphi中编程的需要该类可以代表一个数据集。

TTable组件代表数据库表。TTable直接由TDBTable继承而来,它继承了它的祖先(包括TObject, TPersistent,TComponent, TDataSet, TBDEDataSet和TDBDataSet)的所有功能、数据和事件特性(请参考表13.1,表13.2和表13.3中列出的由TDataSet级别拥有的属性)。

注意:“OLE DB是一个公开的规范,提供一个访问所有类型数据的公开标准,建立在ODBC的基础上”。OLE DB 引入了“Provider”的概念,扩展了可以代替非传统数据库的数据容器。

TTable组件是一个不可视的组件(也就是说没有运行时的可视部分),它代表任何数据库中的物理表或者数据集。要使用TTable,从组件面板的Data Access属性页上选择TTable组件放置到窗体上,在Object Inspector中修改DatabaseName和TableName特性。在13.3.2节“使用数据库窗体向导”中,被向导添加进来的TTable组件的DatabaseName特性为Renegades,TableName特性为dbo.PLAYER_STATISTICS。如果想看这些特性值,打开由向导创建的窗体,选择窗体上的Table组件,按F11键将Object Inspector调到前台(记住Object Inspector中的特性是以字母为顺序的,在Object Inspector上边的对象选择器组合框表示当前被显示特性的组件。如果您在向导例子中使用不同的BDE别名和表那么结果将不同)。

13.7.1  SessionName和DatabaseName特性

每一个表组件都包括一个SessionName和DatabaseName特性。DatabaseName特性是一个别名,代表一个数据库,或者一个TDatabase组件名。在后面的实例中,TDatabase组件将指向一个实际的数据库别名(请参考TDatabase组件部分以了解更多的信息)。

注意:全局的Session对象暗示了单对象实例的实体,被指定为singleton(单元素)对象。一个单元素是一个全局对象,它只有一个实例;实际上您可以使用TSession组件创建Session对象的多个实例。一个单元素对象的一个例子是Application对象。每一个Delphi可执行程序都有一个Application对象的实例。经常地,单元素构造函数受到了一定的限制以禁止构造附加的实例。实例通常通过类方法(使用一个岗哨验证只有一个实例被创建)创建。

您可以将SessionName设置为一个Session组件、保留空白或者从组合框中选择Default。如果您没有指定一个Session组件,那么将使用DBTables.pas的初始化部分创建全局Session对象。如果没有使用TDatabase组件,那么将创建一个Database对象并指向DBDataSet引入的公有只读特性Database。因此,不管您是否使用Session和Database组件,它们都将被自动地创建和使用(TSession和TDatabase提供很重要的服务。请参考关于TSession和TDatabase部分获取有关使用Session和Database组件的更多信息)。

13.7.2  Table属性

现在您已经了解了TTable组件的DatabaseName, SessionName和 TableName特性。Table组件公布了一些附加的特性。MasterSource, MasterFields和FieldIndexNames特性使您可以定义一个主细节关系。MasterSource指向一个DataSource组件。MasterFields指向与本数据集相关联的主数据集中的字段,FieldIndexNames指向主数据集中的索引字段(请参考14章中的主细节关系部分了解更多有关本主题的信息,以便使我们可以更好地讨论DataSources 和Fields)。

13.7.3  Fields

TDataSet引入了Fields集合,表示数据集的字段。在本上下文中的字段是指单个记录和列的交点。您可以通过FieldByName方法或者Fields集合访问一个表中的每个字段。

procedure ShowFieldNames;

var

I : Integer;

begin

// walk fields

for I := 0 to Table1.Fields.Count - 1 do

ShowMessage( Table1.Fields[I].FieldName );

 

ShowMessage(

'Table1.FieldByName(''ID'').FieldNo=' +

IntToStr(Table1.FieldByName('ID').FieldNo) );

end;

Fields集合和FieldByName方法都返回一个TField对象。前面所列的程序说明了怎样迭代集合中的每一个字段,第二个语句显示了ShowMessage对话框(如图13.9所示),图中包含了创建FieldNo返回结果的语句和FieldNo值。

图13.9  FieldByName返回一个字段对象,此语句和结果用于返回

PLAYER_STATISTICS表中ID字段的FieldNo值

当您使用数据库窗体向导的时候,它创建了静态的TField组件。这些是非可视的组件(属于DataSet)。您可以在窗体(或者DataModule,数据模块)类定义的开始部分找到这些组件。它们被指定为静态字段,因为它们在是在设计时被创建的并且它们的特性在DFM中有定义。您也可以使用Fields编辑器创建静态的TFields组件。如果您不想在设计时创建TField组件,那么DataSet对象将在运行时创建这些组件(在DataSet被实际地打开之前)。(CreateFields方法的调用是由TBDEDataSet的InternalOpen方法产生的)。让我们转移到TQuery组件,TQuery组件也可以使用动态的或者静态的字段,在介绍动态和静态字段对象的章节再回过头来讨论字段对象。

13.8  TQuery组件

TQuery组件是TTable组件的姐妹组件。TQuery组件有一个SQL特性,而不是通过指定一个TableName特性的值直接指向一个表。SQL特性是一个TStrings对象,该对象进行有效的SQL编码。您在Strings编辑器(请看图13.10,Strings特性编辑器中的一个SQL例子)中所写的实际SQL语句要取决于您所使用的数据库引擎的需要。BDE引擎是ANSI-92 SQL兼容的,但是所使用的精确的SQL文本随所使用的数据库而改变。

注意:ANSI-92 SQL中ANSI指的是American National Standards Insititute(美国国家标准协会),92指的是采用该标准的年份,SQL指的是Structured Query Language(结构化查询语言)。ANSI SQL委员会是一个团体,该团体包括数据库卖主、专家和参与制定SQL标准组成的爱好者。

图13.10  Strings编辑器显示了用于修改TQuery组件的SQL特性

您可以交替地使用TTable组件和TQuery组件。比如说,您可以使用一个TTable或者TQuery组件在一个数据库中创建一个表,进行删除、编辑、插入、查找或者更新记录。因为TQuery和TTable组件有同样的组件,所以您可以使用TQuery对象调用同TTable对象一样的方法。因此您可以使用两种方法中的一种使用TQuery删除一个记录:找到正确的记录,调用Delete方法或者编写DELETE SQL语句,并使用ExecSQL方法运行查询。

13.8.1  编写SQL SELECT语句

正如最后一段所提到的,您可以使用下面两种方法之一对表进行相似的操作:使用从TDataSet继承的方法或者编写SQL语句。为了说明这个问题,使用由向导生成的Player Statistics窗体并用一个TQuery组件取代原来的TTable窗体。下面的步骤做了这些修改。

1.运行Delphi。

2.打开Player Statistics工程,选择向导窗体(或者使用任何数据库窗体和一个TTable组件)。

3.从组件面板的Data Access属性页中双击TQuery组件(该组件位于从左边起第三位,该图标上有SQL文本)将该组件添加到数据库窗体中。

4.在窗体上找到TDataSource组件,该组件已经由向导添加了,单击该组件,选择该组件。按F11按钮显示Object Inspector窗口。

5.该DataSource组件有一个DataSet特性。从特性编辑器(一个组合框)中选择Query组件,设置DataSet特性值。

6.单击Query组件。在Object Inspector中找到SQL特性,单击省略按钮打开Strings编辑器(回想一下,SQL特性是一个TStrings对象)。

7.输入SQL语句(请参考图13.10所示):SELECT * FROM dbo.PLAYER_STATISTICS(如果您使用其他的表请用您所使用的表替代dbo.PLAYER_STATISTICS)。

8.在数据库窗体的代码编辑器中,找到FormCreate事件方法。将代码Table1.Open改为Query1.Open(如果在代码编辑器中还没有这个事件方法,双击Form组件中的OnCreate事件特性创建一个FormCreate事件方法)。

9.按F9按钮运行该例子。

注意:值得一提的是如果Table和Query组件没有通常的DataSet祖先,DataSource组件需要设置两个特性:一个为Query另一个为Table。这会使组件DataSource的代码变得复杂。使用通常祖先TDataSet将简化TDataSource组件的代码。

第1步到第9步的结果等同于使用Table组件。当您使用一个TTable组件的时候,TableName特性用于创建SQL语句,类似于第7步在Strings编辑器中输入的SQL语句(如果运行程序的时候打开SQL Monitor,您将会看得一清二楚。请看图13.11中的第97步)。

图13.11  SQL Monitor显示当一个数据库被打开的时候在后台所进行的所有工作。

    图中的第97步显示了对于Table组件冗长的SELECT语句是怎样被

 组合起来的,该语句详细地列出了所有的列名称,而不是使用*

SELECT语句的一般格式是:

SELECT fieldname1[ ,fieldname2, fieldnamen| *] FROM tablename

SELECT和FROM是SQL语言中的关键字。斜体的fieldname参数代表将在结果的数据集中返回的字段。tablename是数据库表名,结果数据集就是从该表中获取数据。

SQL是一种编程语言,它有自己完整的语法。已经有许多相当好的、很完整的图书介绍SQL语言了。除了要考虑您的数据库厂商所使用的实际SQL之外,第19章“创建查询生成器”介绍了SQL查询生成器,提供了许多常用的SQL构成。

13.8.2  Open与ExecSQL方法

Query组件增加了一个附加的方法:ExecSQL,这是Table组件中没有的。当您定义了一个SELECT SQL语句的时候,Query的结果是一个结果集。对于SELECT语句使用Open方法。当您执行一个INSERT, DELETE, UPDATE, CREATE TABLE,DROP TABLE或者任何其他的非SELECT操作(这些操作不返回一个结果数据集)的时候,使用ExecSQL方法。

13.8.3  RequestLive特性

除了SELECT查询之外的所有查询都是只读的。如果可以的话,RequestLive特性将返回一个可修改的数据集。比如说,如果您编写一个同类的查询(该查询在结果数据集中只包含一个表),如果RequestLive设置为True那么查询可以返回一个可修改的查询。但是,如果您创建嵌套的查询或者联合两个或者更多的表——异类的查询——那么查询组件将不能返回可以修改的数据集(请参考第19章关于嵌套查询和联合查询部分了解更多的信息)。

13.8.4  Params

Query组件有一个Params特性。Params为SQL语句中可代替的参数,您可以在定义完SQL语句之后将代码添加到Params中。比如说,如果您只是想返回一个精美的结果数据集,那么您可以定义一个参数化的WHERE语句,在运行时将参数传递进来。使用已经介绍的SELECT,可以添加WHERE子句限制返回行的数目。

SELECT *

FROM dbo.PLAYER_STATISTICS

WHERE ASSISTS > :ASSISTS_PARAM

WHERE子句过滤了结果数据集,只返回那些符合这个子句条件的记录。WHERE ASSISTS>:ASSISTS_PARAM表示当Query被打开的时候只有其值大于ASSISTS_PARAM的记录才被返回。冒号(:)作为名字的第一个字符标定ASSISTS_PARAM作为Query组件的一个参数。

当您在Strings编辑器中输入SQL语句之后,需要在Collection编辑器中完成对Param的定义。单击Query组件中的Param特性旁边的省略号按钮,打开Collection编辑器。Collection编辑器和获得输入焦点的ASSISTS_PARAM参数如图13.12所示。设置DataType为ftInteger——ASSISTS字段的类型——并且设置ParamType为ptInputOuput。

图13.12  Collection编辑器和Object Inspector(ASSISTS_PARAM被选择了)

要给参数赋值,使用Query对象的ParamByName方法,如下所示:

Query1.ParamByName('ASSISTS_PARAM').AsInteger := 6;

Query1.Open;

上面这段代码将使Query组件传递如下所示的SQL语句到Borland数据库引擎中和数据库本身上。

SELECT *

FROM dbo.PLAYER_STATISTICS

WHERE ASSISTS > 6

在表PLAYER_STATISTICS中,只有超过6个助理的比赛者所在的记录才被返回。

13.8.5  UpdateObject特性

TBDEDataSet引入了UpdateObject特性。UpdateObject属于TDataSetUpdateObject类。符合UpdateObject特性的组件只有TUpdateSQL组件。一个UpdateSQL对象可以被赋予一个TTable, TQuery或者TStoredProcedure的UpdateObject对象。TUpdateSQL组件提供了一种超越SQL-92限制的机制。

如果一个数据集由于某种选择是只读的,或者基于查询的类型,UpdateObject可以被用于在后台更新相关的表。如果使用了CachedUpdates,那么在UpdateSQL语句中的SQL在调用ApplyUpdates的时候运行。请参考关于UpdateSQL组件的部分,那里有一个高速缓存更新和UpdateObject特性的使用的例子。

13.9  TDataSource组件

如果代码在超过一个类中出现,通常都意味着这个公共代码单独作为一个类是很好的选择。其中TDataSource就是这样的类。TDataSource类用于连接数据控件和DataSet。在介绍数据控件部分您将看到每一个数据感知控件都有一个DataSource特性,但是没有直接连接到DataSet。DataSource将连接追踪到DataSet。除了DataSet特性,DataSource引入了AutoEdit、Enabled、State特性和Create、Destroy、Edit、IsLinkedTo方法。TDataSource还有OnDataChange、OnStateChange和OnUpdateData方法。

很明显,TDataSource没有引入数量很大的公有特性和方法。它所做的是阻止这些特性在TDataSet的所有子类中被复制。考虑TQuery组件,在没有数据控件的情况下运行一个查询是合理的。比如说,一个DELETE SQL语句不需要运行有用的控件。还可以这样,可以把TQuery用作数据控件的数据集。有时候将DataSource同一个查询联系起来会很方便,有时候则不。

如果我们返回到Player Statistics向导窗体,DataSource被用于将所有的控件(见图13.8)连接到数据集中。回想一下最后的部分,我们可以将数据集从一个表改为一个查询而不需要修改数据控件的DataSource特性。要弄明白一个数据控件和DataSource特性之间的连接点,请查看EditID(任何TDBEdit)控件的DataSource特性。

图13.13  DataSets、DataSources和data controls之间关系的示

         意图。TDataSource是数据和可视控件之间的连接物

总之,一个基本的数据感知窗体需要一个DataSet、一个DataSource和显示数据与允许用户修改数据的控件。第11章讨论的数据库窗体向导和DBFormWizard组件介绍了需要的连接点。图13.13包含了DataSets、DataSources和数据感知控件之间关系的示意图。

下面的代码示范了怎样使用DataSource.OnDataChange事件特性。

procedure TForm2.DataSource1DataChange(Sender: TObject; Field:

TField);

begin

if(Field <> Nil ) then

StatusBar1.SimpleText := Field.DisplayName + ' changed to ' +

Field.Asstring;

end;

注意:Field.DisplayName可以不同于FieldName;比如说,可以在TField组件的DisplayName特性中输入“PLAYER_NAME”作为Player Name。

当一个记录被修改了并且用户从一个控件转移到另外一个控件的时候将调用OnDataChange事件。程序示范了更新一个StatusBar(状态栏)控件(一个非数据库控件)来显示最后一次的修改。在状态栏显示的文本是字段的DisplayName和修改后的新值。

13.10  TDatabase

TDatabase组件是物理数据库的应用程序表现。当您的应用程序创建一个DataSet组件的时候,同时它也获得一个Database组件,不管您是否明确地在您的应用程序中添加了一个Database组件。Database组件保存有关AliasName、the DatabaseName和the Connected状态的特性。它还引入了批更新和事务处理的方法。只有特定的数据库支持事务处理。对于桌面数据库应用程序来说,使用自动创建的DataSet对象就已经足够了。

PlayerStatistics数据库应用程序显示了一个登录对话框。因为这是个人的实用程序,所以最好跳过登录处理,使启动更快速。要实现这个功能,在向导窗体中添加一个Database组件会很有帮助。下面的步骤添加了一个Database组件,跳过了登录处理。

1.运行Delphi,打开包含向导生成的窗体的工程。

2.从组件面板的Data Access选项卡上添加一个Database组件到窗体上。

3.Database组件的AliasName特性设置为别名Renegades。

4.在DatabaseName特性中输入一个值,给这个Database添加一个名称。您可以使用BDE别名或者创建一个新的别名;对于本例,输入DB作为DatabaseName特性。

5.将LoginPrompt的值修改为False。

注意:数据库组件一个便利的方面是您可以创建一个应用程序级的别名。如果您在开发中想在一个实际的和测试的数据库之间进行切换,您所需要做的是修改数据库组件的AliasName特性,以保证所有的DataSet组件都指向在数据库的DatabaseName特性中所输入的值。

当您运行这个修改后的演示应用程序的时候,您将不再需要登录到数据库。当在没有有效的操作员可以登录的应用程序中使用该策略也是很有效的。与TDatabase组件有关的其他资料可以在第15章和第19章中找到。

13.10.1  CachedUpdates

当您创建一个数据感知窗体修改一个记录的时候,这个记录在您导航到另外一个记录的时候被发送。如果您正在使用事务处理,那么在您提交数据之前,这个数据不会在数据库中被永久修改。

DataSet有一个CachedUpdates特性。如果这个特性是True,那么更新保存在本地高速缓存,在调用ApplyUpdates之前不会将更新发送到数据集。对于桌面应用程序,数据库驻留在同样的计算机上,该应用程序作为客户端应用程序可能不需要高速缓存更新,但是使用驻留在网络上的数据库服务器的应用程序如果没有高速缓存更新可能会对网络造成没有必要的阻塞。记录更新高速缓存对于UpdateSQL(一个对其他只读数据集执行更新的辅助组件)组件也是很重要的。

13.10.2  事务隔离级别

事务隔离级别表示在一个事务中所出现的多少东西可以被与该数据库同步的其他事务看见。TDatabase.TranIsolation特性可以是三种状态之一:tiDirtyRead, tiReadCommitted或者 tiRepeatableRead。DirtyRead允许其他的事务读取由其他的事务所做的未被授权的修改;未被授权的修改可能被退回,使被其他事务读取的数据无效。Read-Committed是默认的隔离级别,此级别允许其他的事务读取被授权的永久的修改。Repeatable-Read隔离级别只允许数据库读取一次,保证数据的事务视图在事务修改数据之前不改变。

注意:在默认情况下,dBase和Paradox数据库类型需要tiDirtyRead TranIsolation级别。您只有在使用事务的时候才修改这个值,如果需要,比如说,还可以使用UpdateSQL组件。

如果您使用BDE,那么隔离级别可以用TDatabase组件来指定。您没有必要使用BDE;例如ADO数据组件没有使用BDE。指定与默认值不同的合适的事务隔离级别的可选方法是为非BDE组件提供的。

13.11  TSession

Session组件用于当应用程序中管理多数据库连接时。如果您在应用程序中没有添加一个Session组件,那么将使用默认Session的对象(如DBTables.pas的单元初始化部分中所示例的)。Session用于标准的数据库应用程序(这种应用程序访问驻留在不同网络上的多Paradox表)和多线程数据库应用程序。

默认的Session对象和其默认的状态对桌面数据库应用程序来说已经足够了。请参考第15章和第19章中有关Session的部分了解更多的资料。

13.12  TBatchMove

假设您以一种格式接受数据,并想引入到您的主应用程序数据库中。考虑比赛者统计数据库。在下落和弹起中,曲棍球比赛开始,球队的花名册有了改动。这个球队的花名册以Microsoft Access的格式发送给您,您需要将此花名册引入到SQL Server数据库中。BatchMove可以在类似于这里假设的情形下使用。

BatchMove组件允许您指定一个源和目标的数据集,源可以是一个查询或者一个表,目标必须是一个表(因为BatchMove组件不生成SQL)。该组件包括Mode特性(batAppend, batAppendUpdate, batCopy,batDelete或者batUpdate),可以将一批记录从源数据集移动到目标数据集中。这两个数据集不必在相同的数据库中。

一个附加的好处,BatchMove组件有一个Mappings特性(一个TStrings对象),如果在表中的字段名称不匹配它则允许您指定字段映射。给定Access数据库以及ID, FIRSTNAME和LASTNAME字段,您可以使用BatchMove组件将Access数据移动到SQL Server 的PLAYER_STATISTICS表中。

1.给Access数据库定义一个ODBC别名和BDE入口。

2.在比赛者统计向导窗体中添加一个TBatchMove和TQuery组件。

3.定义查询返回ID字段和一个计算字段(此字段将FIRSTNAME和LASTNAME以及Query的SQL特性连接起来)。如下所示。

SELECT ID, FIRSTNAME + ' ' + LASTNAME AS PLAYER_NAME from

Players

4.将Query的DatabaseName特性设置为第1步创建的别名。

5.将BatchMove Mode特性设为batAppend;这将告诉BatchMove将Access数据库中的所有比赛者名称添加到SQL Server 的PLAYER_STATISTICS表中。

6.改变BatchMove Destination特性使其指向由窗体向导添加的原始表。

7.将BatchMove Source特性改为第2步添加的查询。

8.在窗体上添加一个按钮,将按钮的Caption特性设置为Load。

9.在按钮的OnClick事件处理程序中,添加文本BatchMove1.Execute;。

10.按F9按钮运行应用程序。

当您单击名为Load的按钮的时候,将运行语句BatchMove.Execute。TBatchMove组件将试图把Access数据库中的所有记录添加到SQL Server数据库中。

只要源数据集有对于此数据集组件来说具有有效的数据,则此方法就可以工作。文本文件和其他数据源窗体都将工作得很好。当然,您可以使用两个表来移动数据,迭代源数据集中每一个记录,将记录添加到目标数据集中,但是BatchMove使用起来更容易、更有效。通过添加一个动态的映射字段方法,您可以很容易地创建一个定制的数据导入和数据转变应用实例,这将映射源数据集到任何目标数据集,并且在闲置状态下导入数据。

13.13  TUpdateSQL

根据程序员的设计或者因为您正在使用的数据库引擎的ANSI-SQL标准所强加的限制,Query或者StoredProcedure可能是只读的。我们将用\Program Files\Borland\BorlandSh- ared\Data中的Customer.db和Orders.db数据库例子来说明;如果您编写一个多表的查询您将得到只读的数据集。

注意:从多于一个表中获取数据的查询是异类查询;这不同于从一个表获取的dataset。单表dataset是同类的。异类查询返回只读的结果数据集。

SELECT * FROM Customer, Orders WHERE Customer.CustNo =

Orders.CustNo

上面这种查询方式返回表Customer中与表Orders中的相匹配的所有行,通过表customer的编号来匹配。查询的结果是,每次在表customer中有匹配行的时候将重复这个customer数据,但是Orders表中的数据只出现一次。

13.13.1  创建一个样本UpdateSQL应用程序

样本浏览器窗体(请参考图13.14)对于查看数据是很有用的,但对于其他没有用处,因为查询是只读的(由ANSI-92 SQL标准定义)。这看起来是一种限制。很明显查询知道每一列从哪里获取。UpdateSQL使用CachedUpdate允许您避免这种强制性。要通过数据控件修改只读的异类数据集所需要的是使用CachedUpdates和给数据集的OnUpdateRecord事件特性指定一个事件处理程序或者给数据集的UpdateObject特性指定一个TUpdateSQL组件。下面是摘自DBTables的代码,设置了编辑只读数据集所需的条件。

图13.14  CachedUpdates 和UpdateSQL演示应用程序

function TBDEDataSet.GetCanModify: Boolean;

begin

Result := FCanModify or ForceUpdateCallback;

end;

 

function TBDEDataSet.ForceUpdateCallback: Boolean;

begin

Result := FCachedUpdates and (Assigned(FOnUpdateRecord) or

Assigned(FUpdateObject));

end;

注意:Assigned过程类似于与Nil的比较。Assigned内建于编译器中。如果您使用Code Editor菜单并选择Find Declaration项,那么光标将被设置到System.pas单元的开头部分。

GetCanModify返回FCanModify域和ForceUpdateCallback返回值相或的结果。如果FCachedUpdates为True并且Assigned(FOnUpdateRecord) 或者Assigned(FUpdateObject)为True那么ForceUpdateCallback为True。记住Assigned方法相当于检查一个Nil值。要说明TUpdateSQL组件:创建一个可以多查询的并有一个DBGrid组件的窗体(如图13.15所示为所用组件的设计时视图)。

请遵循下面的步骤创建这个窗体:

1.创建一个新的应用程序,放置一个TMainMenu组件、TDBGrid组件、TQuery组件、TDataSource组件、TUpdateSQL组件和TABoutDialogBox组件(这在第10章中已经创建了)。

2.将DBGrid的Align特性设置为alClient。

3.定义MainMenu组件,使其包含一个Record菜单和一个包含About菜单项的Help菜单,其中Record菜单又包含Apply、Cancel和Exit子菜单项。

4.指定TDataSource.DataSet特性为Query组件。

图13.15  UMasterAppDemo窗体的设计时视图,列

         出了CachedUpdates 和TUpdateSQL组件

5.Query组件特性的设置如下:DatabaseName =DBDEMOS, SessionName = Default, UpdateObject = UpdateSQL(组件),CachedUpdates = True, RequestLive = True,使用Strings编辑器设置SQL特性,添加下面的SQL语句:

SELECT C.CustNo, C.Company, O.* FROM CUSTOMER C, ORDERS O

WHERE C.CUSTNO = O.CUSTNO ORDER BY C.Company ASC

SQL语句选择了表Orders中的所有行和列,选择了Customer表中的所有行和CustNo与Company列;在这里CUSTOMER.CUSTNO 和ORDERS.CUSTNO字段的值相同,结果数据集中的记录以company字段按字母顺序排列。

6.创建TQuery.OnUpdateRecord事件方法。

7.将TDBGrid.Datasource特性设置为DataSource组件。

8.将TQuery.Active特性更改为True;如果前面的所有设置都是正确的,那么在设计时数据就在栅格中了。

9.右击DBGrid组件,选择组件编辑器编辑DBGrid组件;弹出组件编辑器的菜单项是Columns Editor…(如图13.16所示)。

图13.16  DBGrid 的Columns Editor是一个组件编辑器,

          右击栅格控件从弹出的菜单中可以调出此编辑器

10.保留TQuery.Active特性值为True,右击Columns editor(如图13.16所示),单击Add All Columns项,Query的结果数据集的所有列将被添加进来。

11.保持Column editor打开,依次单击CustNo和Company列,按F11打开Object Inspector,将列的ReadOnly特性设置为True。

12.数据集中有两列CustNo:一个来自Customer表,另外一个来自Orders表。单击与Orders表CustNo_1有关的CustNo,按Delete键删除这个字段(我们不想让用户无意中更改索引字段)。

完成第1到第12步后,您将编写代码实现所有的功能。很幸运的是代码不是很多;大多数代码都由Delphi产生。

13.13.2  编写UpdateSQL应用程序代码

UMasterAppDemo的代码如下所示,该代码示范了UpdateSQL组件。大多数代码是您在上面完成12个步骤时由Delphi添加的。

unit UMasterAppDemo;

// UMasterAppDemo.pas - Demonstrates CachedUpdates and the

UpdateSQL component

// Copyright (c) 2000. All Rights Reserved.

// by Software Conceptions, Inc. Okemos, MI USA (800) 471-5890

// Written by Paul Kimmel

 

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,

Forms, Dialogs,

Grids, DBGrids, DB, DBTables, Menus, UAboutBoxDialog;

 

type

TForm1 = class(TForm)

Query1: TQuery;

DataSource1: TDataSource;

DBGrid1: TDBGrid;

UpdateSQL1: TUpdateSQL;

MainMenu1: TMainMenu;

Record1: TMenuItem;

Apply1: TMenuItem;

Cancel1: TMenuItem;

N1: TMenuItem;

Exit1: TMenuItem;

Help1: TMenuItem;

About1: TMenuItem;

AboutBoxDialog1: TAboutBoxDialog;

procedure Query1UpdateRecord(DataSet: TDataSet;

UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

procedure Apply1Click(Sender: TObject);

procedure Cancel1Click(Sender: TObject);

procedure About1Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

uses

TypInfo;

{$R *.DFM}

{$I UMasterAppDemo.Inc}

 

procedure TForm1.Query1UpdateRecord(DataSet: TDataSet;

UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

if( UpdateKind = ukModify ) then

begin

UpdateSQL1.Apply(UpdateKind);

UpdateAction := uaApplied;

end

else

begin

UpdateAction := uaSkip;

ShowMessage( Format( NotImplemented, [GetEnumName(

TypeInfo(TUpdateKind), Ord(UpdateKind))]));

end;

end;

 

procedure TForm1.Apply1Click(Sender: TObject);

begin

Query1.Database.ApplyUpdates( [Query1] );

end;

 

procedure TForm1.Cancel1Click(Sender: TObject);

begin

Query1.CancelUpdates;

end;

procedure TForm1.About1Click(Sender: TObject);

begin

AboutBoxDialog1.Execute;

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

UpdateSQL1.ModifySQL.Text := ModifySQL;

Query1.Open;

Query1.Database.TransIsolation := tiDirtyRead;

end;

end.

大约在上面所列程序的中间,在关键字implementation之后是我们插入程序的开始处。{$I UMasterAppDemo.inc}告诉Delphi编译器当编译的时候将UMasterAppDemo.inc文件中的代码直接放到单元中。这是一种很好的技术,可用于获取难看的源字符串和其他有用的东西,但是在视觉上不具吸引力,内容在看不见的地方(UMasterAppDemo.inc代码在本章中UMasterAppDemo单元代码分析的后面,就在后面一点)。接下来的是OnUpdateRecord事件处理程序;在当前编写的处理程序中它仅处理修改更新。TUpdateSQL.Apply( UpdateKind )方法使Param值被设置(您将立刻获得这些值),并调用ExecSQL方法。最后Var参数UpdateAction的值被设置为uaApplied。设置UpdateAction的值为uaSkip,跳过Insert和Delete的更新。接下来显示一个简短的消息告诉开发者没有定义Insert和Delete行为。

注意:如果您没有在设计时为应用程序添加一个数据库组件,那么在运行时将由Delphi动态创建一个数据库对象。

ApplyClick事件处理程序用于处理Record中的Apply菜单的单击事件。它调用TDatabase.ApplyUpdates方法,传递一个数据集数组。数组中的每一个数据集都用来调用各自的ApplyUpdates方法。Record中的Cancel菜单调用TQuery.CancelUpdates方法来取消所有已经应用的高速缓存修改。FormCreate事件方法将TUpdateSQL.ModifySQL特性设置为ModifySQL资源字符串、打开一个查询和将Database的TranIsolation特性设置为tiReadDirty。

下面是UMasterAppDemo.inc文件的源代码:

// UMasterAppDemo.inc

 

resourcestring

NotImplemented = '%s not implemented!';

DeleteSQL = 'DELETE FROM tablename WHERE field = value';

InsertSQL = 'INSERT INTO (field1, field2, etc) VALUES( value1,

value2, value3)';

ModifySQL = 'UPDATE Orders ' +

'SET OrderNo = :OrderNo, SaleDate = :SaleDate, ' +

'ShipDate = :ShipDate, EmpNo = :EmpNo, ShipToContact =

:ShipToContact, ' +

'ShipToAddr1 = :ShipToAddr1, ShiptoAddr2 =

:ShipToAddr2,

ShipToCity = :ShipToCity, ' +

'ShipToState = :ShipToState, ShipToZip = :ShipToZip,

ShipToCountry = :ShipToCountry, ' +

'ShipToPhone = :ShipToPhone, ShipVia = :ShipVia, PO =

:PO, Terms =

:Terms, ' +

'PaymentMethod = :PaymentMethod, ItemsTotal =

:ItemsTotal,

TaxRate = :TaxRate, ' +

'Freight = :Freight, AmountPaid = :AmountPaid ' +

'WHERE OrderNo = :OrderNo';

ModifySQL资源字符串包含一个参数化的UPDATE SQL语句。UPDATE 语句的一般格式为:

UPDATE tablename SET fieldname1 = value1 [, fieldname2 = value2,

… fieldnamen = valuen];

tablename是目前正在被更新的表名。UPDATE和SET是关键字。关键字SET之后是成对的fieldname名和value。fieldname代表表中的一个字段,Value代表与字段类型匹配的数据。在本例中,参数value在等号操作符的右边。在参数前面加一个“:”表示后面跟的是参数。在UpdateSQL语句中的参数必须和字段名匹配,以便使UpdateSQL组件可以自动地填充参数值。

包含文件当作一种便利的辅助工作机制来使用。正如您可以从代码中看到的,SQL语句资源字符串很长并且有点混乱,SQL语句资源字符串没有加入到单元中。

13.14  小  结

Delphi有一个组织很好的、有层次的体系结构。从Delphi的外观看,有点像Visual Basic。深入地了解Delphi,这种错觉就消失了。第13章介绍了很少用于开发数据库应用程序的组件和控件。您看到了数据库窗体向导怎样以很少的步骤创建实用的数据库应用程序(第11章介绍了一个自动做这些工作的组件)。从一个开发者的角度来看,复杂性都已经被隐藏了。从一个工具开发者的角度来说,就要看您想要深入的程度了。

如果对类和层次进行细分就会发现,Delphi 的VCL只不过是Object Pascal。第13章介绍了所有的Data Access组件,包括数据控件和两个演示应用程序。Delphi还有其他更多的方面需要探讨。从“Delphi 6应用开发指南”这本书的中间部分开始,我们将开始转移到建立应用程序部分。

 

相关阅读 更多 +
排行榜 更多 +
谷歌卫星地图免费版下载

谷歌卫星地图免费版下载

生活实用 下载
谷歌卫星地图免费版下载

谷歌卫星地图免费版下载

生活实用 下载
kingsofpool官方正版下载

kingsofpool官方正版下载

赛车竞速 下载