Delphi 数据库开发完美教程_第5章_连接数据库


2011-9-17 1 第5章 连接数据库 本章学习要点  理解Delphi的数据集控件的基本概念和术语  了解Delphi数据集的基本属性和方法  学习Delphi数据集的浏览  掌握Delphi基于ADO的数据库连接方法 2011-9-17 2 5.1数据集控件  在Delphi中访问数据的基本单元是数据集对象,数据集是以行 和列的形式组织起来的数据的集合;其中,每一列具有相同的数 据类型,而每行是由各列指定类型的数据组成的。 为了更好地 理解数据集的概念,现将几个与数据集相关的概念和术语说明如 下:  数据集 一个分离的数据记录的集合。其中,每个记录包含多 个字段。各个字段可以是不同数据类型(如整数、字符串、十进 制数和图像等)的数据。数据集由VCL的TDataSet表示。  表 一种特殊类型的数据集。表一般是一个实际存储在磁盘 上、包含有数据记录的文件。VCL的TTable类中封装了它的各种 功能。  查询 也是一种特殊类型的数据集。它可以被看作是执行了特 殊命令后所产生的“内存表”,这些命令一般是对物理表或表集的 操作。在VCL中由TQuery类来处理查询。 2011-9-17 3  数据库:指磁盘中的一个目录(在处理如Paradox、dBASES文件 这样的非服务器数据的情况下),或是一个SQL数据库(当使用 SQL服务器时)。在一个数据库中可以包含多个表。在VCL有一 个TDataBase类。  索引 用于数据库表排序的规则。所谓基于特定字段建立索引 是指利用该字段的值作为表的排序依据。在TTable中包含了一些 对索引进行操作的方法和属性。  如果应用程序没有使用了TDatabase控件,则系统根 据数据集控件DatabaseName属性创建一个临时的数据 库。它们按照用途可分为三类: TDecisionquery TADOCommand TADODataSet TADOTable TADOQuery TADOStoredProc TADOConnection TCustomADODataet TNestedTable TTable TQuery TStoredProc TDBDataSet TBDEDataSet TClientDataSet TDataSet 2011-9-17 4  第一类是用于BDE 体系结构的数据集控件,包括 TNestedTable、TTable、TQuery、TStoredProc,以及用于数 据库决策的TDecisionquery控件;  第二类是ADO体系结构的数据集控件,包括TADODataSet、 TADOTable、TADOQuery和TADOStoredProc;  第三类是平面文件体系结构的客户端数据集控件,包括 TClientDataSet。  数据集控件之间的继承关系如图5-1所示。  5.1.1 数据集的属性  数据集的属性很多,最常用的有状态(State)属性、书签 (Bookmark)属性和过滤方面的属性(例如FilterOptions、 Filtered和Filter等)。下面将介绍它们的含义和用法。 2011-9-17 5  1.状态属性  有时候,需要知道一个数据库表当前为 编辑模式还是添加模式,或是否处于活动状态, 这些信息都可以通过查看TDataSet的State属性 来获取。State属性的类型为TDataSetState,该 类型的取值可以是表5-1中所列的任意一种。 表5-1 数据集的的属性取值及含义 2011-9-17 6 数据集正处在打开过程且没有结束,当数据集进行异步数据读取 时进入此状态 正在打开状 态 dsOpening 数据集正对记录中的计算结果处理OnCalcFields事件,此状态只 适用于客户端数据 内部计算状 态 dsInternalCalc 当一次有多个数据记录被读入内存时进入此状态,此时数据控件 不能更新,事件也不能触发 成批录入状 态 dsBlockRead 数据集正在进行过滤操作过滤状态dsFilter 内部使用旧值状态dsOldValue 内部使用新值状态dsNewValue 内部使用现值状态dsCurValue 数据集正在处理OnCalcFileds事件,此时不能修改非计算字段的 值 计算字段状 态 dsCalcFields 只适用于TTable和TclientDataSet。数据集已打开,可以设置范 围和键值,并且可以调用GotoKey方法 键标状态dsSetKey 此时可以插入一条新的记录插入状态dsInsert 此时为编辑状态,可以修改数据编辑状态dsEdit 数据集已打开,可以浏览数据但不能修改数据浏览状态dsBrowse 数据集已关闭,不能访问它的数据关闭状态dsInactive 含义状态关闭状 态 属性值 2011-9-17 7  数据集的State属性是只读属性,在程序中可以读取State属性值,但不能设 置State属性。  当一个应用程序打开一个数据集的时候,数据集的State属性默认地被设为 dsBrowse,及数据集处于浏览状态。要使数据集进入其它的状态,就要调用 相应的方法。  2.书签属性  书签就是在数据集的某个位置做的一个标记,它的作用是以后可以快速方 便地回到书签的所在位置。数据集提供了许多的属性和方法用于管理书签。  数据集的Bookmark 属性值代表的是当前记录的书签,可以通过设置 Bookmark属性值来指定当前书签。  FreeBookmark:删除一个书签。当书签不用的时候,调用FreeBookmark方 法删除分配给某个书签的资源。 2011-9-17 8  3.过滤属性  一个数据库应用程序往往只对数据集的部分记录感兴趣,例如, 可能只需要对一个学生成绩表中成绩高于90分的学生感兴趣。这 种情况下,可以用过滤技术把符合特定条件的记录过滤出来。不 过,对于一个字段很多的数据集或者过滤条件较为复杂来说,最 好还是使用查询控件来进行过滤。  要对数据集进行过滤,一般需要以下3个步骤:  ① 首先要指定过滤条件:  ② 然后设置FilterOptions属性的有关选项。根据具体需要而定, 也可以没有;  ③最后把Filtered属性设为True。  以后如果不要进行过滤,只要把Filtered属性设为False即可。  (1)设置过滤条件与OnFilterRecord事件  指定过滤条件有两种方式:  一是设置Filter属性;  二是在处理OnFilterRecord事件的句柄中给出过滤条件。 2011-9-17 9  Dataset1.Filter:='"State"="MA"';  也可以让用户来动态地指定过滤条件,如:  Dataset1.Filter:=Edit1.Text;  甚至还可以把上述两行代码结合起来:  Dataset1.Filter:='"State"='+Edit1.Text;  设置了过滤条件后,只要把Filtered属性设为True,过滤即有效。  (2)设置过滤选项  FilterOptions属性用于设置过滤的选项。这个属性是一个集合,可以是空集 (默认),也可以取如表5-2所示的值。  表5-2 FilterOptions的取值及含义 对于字符串类型的字段必须全部匹配,不允许部分 匹配 foPartialCompare 比较字符串时忽略大小写foCaseInsensitive 含义取值 2011-9-17 10  5.1.2 数据集的操作  对数据集的操作有很多,下面介绍一些常用的操作,如打开与关闭数据集、 浏览记录、查询记录、搜索特定记录、修改记录、插入新记录、删除记录 等。  1.开关数据集  在对数据集进行任何操作之前,必须首先将数据集打开。要打开数据集,可 以把Active属性设为True即可,例如:  MyTable.Active:=True;  注意:在结束对数据集的操作以后,或者是要改变数据集的一些其它的属 性,比如修改TTable的TableName前,需要关闭数据集。  关闭数据集有两种方法:  ① 将数据集的Active属性设为False,例如:  MyTable.Active:=False;  ② 调用数据集的Close方法,例如:  MyTable.Close;  2.浏览记录  数据集提供的浏览数据的方法或属性如表5-3所示。  表5-3 数据集浏览记录的方法和属性 2011-9-17 11 如裹这个属性返回True,表示现在已到了 数据集的末尾位置 Eof属性(只读) 如果这个属性返回True,表示现在已到了 数据集的开始位置 Bof属性(只读) 使距离当前记录若干行的记录成为当前记 录 MoveBy方法 使前一条记录成为当前记录Prior方法 使下一条记录成为当前记录Next方法 使最后一条记录成为当前记录Last方法 使第一条记录成为当前记录First方法 功能方法或属性 2011-9-17 12  (5)MoveBy方法  调用数据集的MoveBy方法使数据集中的另一条相隔指定距离的 记录成为当前记录。MoveBy方法需要传递一个参数,指定相隔 的行数,正数表示向记录编号增大的方向移动,负数表示向记录 编号减小的方向移动。程序代码如下:  MyTable.MoveBy(8);  (6)Eof属性  Eof是数据集的只读属性。用于判断当前记录是否到了数据集的 末尾。如果Eof属性值为True,表示现在已到了数据集的最后一 条记录。在遍历数据集的记录时一般需要用到Eof属性。  进行下列任何一个操作,都将会把Eof属性设为True。  打开一个空的数据集;  调用了Last;  调用了Next,而现在已经在数据集的最后一条记录;  调用了SetRange,而范围是无效的。  在其它情况下,Eof属性都将返回False。  在调用数据集的Next方法的循环中,通常要读取EOf属性,以此 来判断当前记录是否已处于数据集的最后一条记录,以避免对不 存在的记录进行操作。程序代码如下 2011-9-17 13  MyTable.DisableControls;  Try  MyTable.First;  While not MyTable.EOF DO  Begin  …  MyTable.Next;  End;  MyTable.EnableControls;  End;  出现异常,也能保证刷新能得到恢复。  (7)Bof属性  Bof也是数据集的只读属性,它的作用和Eof恰好相反。Bof属性用于判断当前 记录是否是数据集的第一条记录。如果Bof属性返回True,表示现在已到了数 据集的第一条记录。在遍历数据集的记录时一般也需要用到Bof属性。  进行下列任何一个操作,都将会把Bof属性设为True。  打开一个非空的数据集。 2011-9-17 14  调用了First;  调用了Prior,而现在已经在数据集的第一条记录;  在其它情况下,Bof属性都将返回False。  程序代码如下:  MyTabte.DisableControls;  Try  While not MyTable.BOF DO  Begin  …  MyTable.Prior;  End;  Finally  Mymable.EnableControls;  End;  3.编辑数据  要编辑数据集的记录,首先要调用Edit方法进入编辑状态,此时 数据集的State属性值将为dsEdit。 2011-9-17 15  注意:如果使用缓存更新技术的话,也就是数据集的 CachedUpdates属性值为True时,调用数据集的Post方法只是将 修改的数据写到缓存中,并不是直接写到数据集中。这时,需要 调用ApplyUpdates方法才能将数据写到数据集中。  如果要取消当前所做的修改,可以按键盘上的Esc键或单击 TDBNavigator控件上的Cancel按钮。  4.插入记录  要在数据集中插入新的记录,首先要调用Insert或Append方法进 入插入状态,此时数据集的State属性值为dslnsert。同样,调用 Insert或Append方法并不一定会使数据集进入插入状态,还要取 决于CanModify属性的值。  一旦进入了插入状态,用户就可以在数据控件中插入一条新的记 录,并输入相应的数据。 2011-9-17 16  5.删除记录  调用数据集的Delete方法将删除当前记录,并且使数据集回到浏 览状态,也就是说,数据集的State属性值将为dsBrowse。当前 记录被删除后,下一条记录就成为当前记录。如果删除的本来就 是最后一条记录,则前一条记录就成为当前记录。  6.修改记录  除了TDBGrid和TDBNavigator等控件外,大部分数据控件只能工 作于数据集的一个或几个字段,而不是整条记录。不过,数据集 提供了若干个方法可以直接修改整条记录,这些方法包括:  AppendRecord方法:类似于Append,但可以给字段赋值,不需 要调用Post。  InsertRecord方法:类似于Insert,但可以给字段赋值,不需要调 用Post。  SetFields方法:且当前记录的字段赋值,需要显式地调用Post。  假设一个数据集中有五个字段,分别是Name、Size、Weight、 Area和Bmp,可以样对它们赋值: 2011-9-17 17  AnimalsTable.InsertRecord(['flatfish','5']);  上述程序在数据集中插入了一条新的记录,并且对前两个字段赋 了值。如果还要对 Weight和Area字段赋值的话,可以用如下的 程序代码。  With AnimalsTable Do  Begin  If Locate('Name','flatfish',IoCaselnsensitive) then  Begin  Edit;  SetFields(NIL,NIL,5,5);  Post;  End;  End;  7.查询特定的记录  (1)Locate方法  Locate用于在数据集中定位一条特定的记录,并使该记录成为当 前记录。Locate方法的语法如下: 2011-9-17 18  function Locate(const KeyFields : string ; const KeyValues : Variant ; Options:TLocateOptions):Boolean;virtual;  从上面Locate 方法的语法可以看出,它需要传递三个参数,第一个是 KeyFields参数,用于指定要按哪些字段搜索,第二个是KeyValues参数,用 于指定每个要搜索的字段对应的值,第三个是Options参数,用于设置搜索选 项。  下面这个例子搜索Name字段的值为“flatfish”的记录:  var  MyLocate:Boolean;  SOpt:TLocatoOptions;  Begin  SOpt:=[IoPartialKey];  MyLocate:=AnimalsTable.Locate('Name','flatfish',SOpt);  End;  (2)Lookup方法  Lookup方法与Locate方法十分相似,也是在数据集中搜索符合条件的记录。 不同的是,如果找到匹配的记录,Lookup能返回该记录中若干个字段的值。 它的语法如下: 2011-9-17 19  function Lookup(const KeyFields:string;const KeyValues:Variant;const ResultFields:string):Variant;virtual;  从上面Lookup方法的语法里可以看出,它需要传递三个参数,第一个是 KeyFidds参数,用于指定要按哪些字段搜索,第二个是KeyValues参数,用 于指定每个字段相应的值,第三个是ResultFields参数,用于指定要返回哪些 字段的值。  下面这个例子是在AnimalsTable中搜索Name字段的值为“flatfish”的记录,并 返回Name、Weight、Area等字段的值。  var  LkResults:Variant;  Begin  With AnimalsTabe Do  LkResults:=Lookup('Name','flatfish','Name;Weight;Area');  End; 2011-9-17 20  5.1.3 数据集的事件  TDataSet的事件主要分为三大类,一类是Before系列,另一类是After系列,还有就是 On系列。具体的事件及含义如表5-4所示。  表5-4 数据集的事件及含义 2011-9-17 21  1.Before系列  Before系列的事件常常用来中止操作。  2.After系列  After系列的事件是发生在操作完成以后,一般用来在状态栏上通知用户一些 情况。如果在删除记录后需要在状态栏里向用户提示删除状态的信息,可以 调用如下的程序代码:  ProcedureTForml.TablelAfterDelete(DataSet:TDataSet);  Begin  StatusBarl.SimpleText:=Format('有%d条记录',[DataSet.RecordCount]);  End;  3.On系列  OnCalcFields事件主要用于给出“计算字段”的值。OnCalcFields事件的发生要 取决于 AutoCalcFields属性的值。  如果AutoCalcFields 属性设为True ,则下列任何一种情况都会触发 OnCalcFields事件。  数据集被打开时; 2011-9-17 22  在数据控件中,输入焦点从一条记录移到另一条记录;  在数据控件中,输入焦点从一个字段移到另一个字段;  当前记录被修改或从数据库中检索了一条记录。  不过,即使AutoCalcFields属性设为False,当数据集中 的任意一个非计算字段的值发生变化时都会触发 OnCalcFields事件。  5.1.4 数据集的派生类  由图5-1可以看出,数据集的派生类可以分为三大类: 第一类是BDE体系结构的数据集控件;第二类是ADO体 系结构的数据集控件;第三类是客户端数据集控件。下 面分别介绍一下这三种控件。  1.BDE数据集  BDE数据集,也就是TBDEDataSet类是从TDataSet继 承而来的,它提供了通过 BDE(BorlandDatabaseEngine)访问数据的能力。 2011-9-17 23  从 BDE 数据集派生的有TNestedTable 类和TDBDataSet 类两大类。而从 TDBDataSet1类又派生了TTable、TQuery和TStoredProc。这一节主要介绍 BDE数据集,读者应当对前面介绍的TDataSet已经有了比较深刻的认识。  和TDataSet一样,TBDEDataSet也是虚拟的和抽象的,也就是说它的许多属 性、方法和事件是虚拟或抽象的。除非要建立自定义的数据集,否则,一般 不需要直接用到TBDEDataSet。  TBDEDataSet重载了TDataSet中涉及记录导航、索引和书签的方法,增加了 一些处理BLOB字段、缓存更新的属性、方法和事件。  (1)CacheBlobs属性  (2)缓存更新  BDEDataSet提供了缓存更新的技术。所谓缓存更新,就是应用程序从数据库 中检索到的数据,在本地缓存中建立一个副本。如果用户对数据进行修改; 那么数据的修改也只是反映在缓存中。以后可以调用BDE 数据集的 ApplyUpdate方法一次性地把所有的修改提交到数据集中。  从缓存更新技术的处理机制中可以看出,缓存更新技术可以明显地提高应用 程序的性能。而且,只要在还没有调用ApplyUpdates之前,可以方便地取消 修改。 2011-9-17 24 BDE数据集中有关缓存更新的属性、方法和事件 如表5-5所示。 表5-5 BDE数据集中有关缓存更新的属性、方法 和事件 2011-9-17 25 (3)TDBDataSet派生类 TDBDataSet类是从TBDEDataSet继承而来的, 它提供了对数据库连接和会话期管理的能力。 TBDataSet类中增加了若干个属性和方法用于管 理数据库连接和BDE会话期,如表5-6所示。 表5-6 TDBDataSet类增加的属性和方法 2011-9-17 26  这里详细介绍一下DatabaseName属性和SessionName属性。  如果应用程序要访问远程数据库服务器如Sybase、Oracle或 InterBase,应当用TDatabase控件来连接数据库,此时,应当将 DatabaseName 属性指定为要连接的数据库,可以设为 TDatabase控件的名称。如果在应用程序中没有显式地使用 TDatabase控件,那么DatabaseName属性应当设为BDE别名。 对于Paradox和dBASE表来说,可以设为表所存放的路径。  SessionName属性用于指定一个BDE会话期对象。如果应用程 序没有显式地使用TSession控件,则不必设置这个属性。如果应 用程序显式地使用了多个TSession 控件,应当设置 SessionName属性来指定是其中的哪一个。  (4)TNestedTable派生类  TNestedTable直接从TBDEDataSet类派生而来,用于访问嵌套 数据表中的数据。TNestedTable类继承了TBDEDataSet类的所 有属性、方法和事件,只增加了一个Create方法,用于创建嵌套 数据表。 2011-9-17 27  2.ADO数据集  ADO数据集,即TCustomADODataSet类,是由TDataSet类派生而来的,提 供了通过ADO控件访问数据库的能力。ADO数据集派生了TADODataSet类、 TADOTable类、TADOQuery类、TADOStoredProc类、TADOCommand类 和TADOConnection类。  TCustomADODataSet重载了TDataSet中涉及记录索引和书签的一些属性、 方法和事件,另外,它本身也增加了一些属性、方法和事件。  (1)ADO数据库连接  TCustomADODataSet增加了几个用于连接数据库的属性,如表5-7所示。  表5-7 TCustomADODataSet增加的用于连接数据库的属性 2011-9-17 28  用户可以利用Connection属性和ConnectionString属性 来连接数据库。如果要用Connection属性来连接数据库 的话,需要先用ADOConnection属性建立与数据库的 连接,然后将ADO数据集的Connection属性指定为 ADOConnection控件的名称,如:  MyADOTable.Connection:=MyADOConnection  如果是用ConnectionString属性连接数据库的话,通过 设置ConnectionString来指明将ADO连接控件与数据集 相连的必要信息,也就是连接字符串。在连接字符串中 需包括远程数据库服务器的名称和OLEDB提供者等信 息,如:  ADOConnectionl . ConnectionString : ='Provider=ProviderRef;RemoteServer=ServerRef';  ADO的ConnectionString的属性值支持下列四种参数设 置 Provider , FileName , Remote Provider , RemoteServer。其它的参数设置(例如用户的身份和密 码)将不被ADO处理而只简单地被传输。 2011-9-17 29  CursorLocation属性指定用于连接的指针是位于客户端还是服务器端。在默认 情况下是位于客户端,即其值为clUserClient。此时指针比较灵活,所有数据 的存取和操作都在本地进行,一些不被数据库服务器正常支持的操作也可以 执行,如排序和过滤等操作。  (2)ADO命令处理  TCustomADODataSet比TDataSet增加了一些属性来处理对数据库的操作命 令,如表5-8所示。  表5-8 ADO数据集处理数据库操作命令的属性 2011-9-17 30  CommandText属性用来说明要使用ADO操作控件执行 的命令内容。CommandText属性值是文本形式的命 令,例如SQL语句、一个数据表格名或者是一个存储过 程名。  在程序中设置CommandText属性的形式如下:  ADOCommandl . CommandText:='DROP TABLE Animals'  如果操作中包含了一些参数(例如在执行SQL语句或者 一个存储过程时的情况),我们可以通过Parameters属 性值来设定。  CommandType属性用来说明在CommandText中所声 明操作的类型。  CommandType的属性值应该与CommandText中所说 明操作相一致, 2011-9-17 31  CommandType的默认值为cmdUnknown,因为cmdUnknown可以适用于所 有操作。在CommandType中显式地指明操作的类型可以提高应用程序的运行 速度,但如果将操作的类型置为cmdUnknown,ADO必须先判断操作的类 型,这样就会减慢运行的速度。  (3)ADO缓存更新  ADO数据集同样也提供了缓存更新技术,就是将ADO数据集中的数据在本地 缓存建立副本,数据的改动只反映在缓存中而不向数据集提交。如果要将数 据的改动提交到数据集中,可以调用UpdateBatch方法。  ADO数据集中用于缓存更新的属性、方法和事件如表5-9所示。  表5-9 ADO数据集中用于缓存更新的属性、方法和事件 2011-9-17 32  ADO数据集的UpdateBatch方法的语法如下:  Procedure UpdateBatch(AffectRecords:TaffectRecords=arALL);  在上面的语法中,参数AffectRecords的取值有4种情况,如表5-10所示。  表5-10 UpdateBatch方法的AffectRecords参数的取值情况 2011-9-17 33  ADO数据集使用缓存更新技术时,首先使用ADOConnection控 件的BeginTrans方法,再使用ADO数据集的UpdateBatch方法。 例如:  MyADOConnection.BeginTrans;  MyADOTable.UpdatcBatch;  3.客户端数据集  与 BDE 数据集一样,客户端数据集(TClientDataSet) 也是由 TDataSet派生而来的,用于平面文件体系结构的数据库应用程 序,不过,它最主要的用途是用于多层体系结构的客户端。  TClientDataSet类是从TDataSet类派生而来的,支持诸如编辑、 搜索、浏览、纠错和过滤等功能。由于客户端数据集在内存中建 立了数据的本地副本,因而操作执行速度很快。同时由于客户端 数据集控件不直接连接数据库,因此客户端数据库应用程序必须 提供获取数据的机制。客户端数据集有两种方式获取数据:  在单层体系结构中,从平面文件中存取数据,独立实现数据集功 能,此时不需要BDE数据库引擎;  TClientDataSet类增加了从数据库读取数据建立数据副本及管理 的属性、方法和事件,如表5-11所示。 2011-9-17 34 表5-11 TCHentDataSet增加的用于建立和管理 副本的属性、方法和事件 2011-9-17 35  5.1.5 SLMIS中数据集的浏览实例  下面利用已学过的数据集中用于浏览数据的方法和属性,创建一个小程序, 其运行后的窗体如5-2所示。  图5-2 数据集浏览窗口 2011-9-17 36  程序源代码摘要如下:  procedure TFrmManagerSearchPSD.RefreshClick(Sender: TObject);  begin  if ADOQuery1.Active then ADOQuery1.Close;  DataSource1.Enabled:=false;  ADOQuery1.Open;  DataSource1.Enabled:=true;  end;  procedure TFrmManagerSearchPSD.FormCreate(Sender: TObject);  begin  ADOQuery1.Open;  DataSource1.Enabled:=true;  end;  procedure TFrmManagerSearchPSD.quitClick(Sender: TObject);  begin  FrmManagerSearchPSD.Close;  end; 2011-9-17 37  procedure TFrmManagerSearchPSD.FormClose(Sender: TObject;  var Action: TCloseAction);  begin  FrmManagerSearchPSD.Release;  end;  procedure TFrmManagerSearchPSD.BitBtn2Click(Sender: TObject);  begin  if ADOQuery1.Active then ADOQuery1.First;  end;  procedure TFrmManagerSearchPSD.BitBtn4Click(Sender: TObject);  begin  if ADOQuery1.Active then  if not ADOQuery1.Bof then ADOQuery1.Prior;  end;  procedure TFrmManagerSearchPSD.BitBtn3Click(Sender: TObject);  begin  if ADOQuery1.Active then  if not ADOQuery1.Eof then ADOQuery1.Next;  end;  procedure TFrmManagerSearchPSD.BitBtn5Click(Sender: TObject);  begin 2011-9-17 38  if ADOQuery1.Active then ADOQuery1.Last;  end;  end. 2011-9-17 39 5.2 ADO数据库连接  ADO是从Delphi5开始新增加的功能,Delphi通过微软的ADO存取各种类型的 数据库,是目前越来越流行的存取数据库的方式,使用ADO就可以无需再使 用BDE了。  5.2.1 基于ADO的体系结构  下面首先介绍一下ADO的来历。Universal Data Access (UDA)是微软公司推 出的对数据库操作的一个策略,提供了快速访问各种数据库的能力,UDA提 供了一种不受限制的能力,通过易用的API接口访问各种数据源(需要与其兼 容的驱动程序),类似Delphi的BDE,这项技术能在一个程序中从多种的数据 源中轻易的访问到数据。UDA 用 Microsoft Data Access Components ( MDAC )来实现,而MDAC 则包括Active Data Objects(ADO) , Open Database Connectivity(ODBC)与OLE DB。ADO是MDAC的应用程序设计接 口,OLE DB则是系统级的接口,定义了一套COM接口,提供了从关联数据 库及文件系统的数据访问能力,ODBC为了向后兼容也包含在MDAC中, ODBC将会被OLE DB替代,因此就目前来说,如果可以直接通过OLE DB操 作的数据库就不要使用ODBC 了,目前OLE DB 可直接用于Microsoft Access,Microsoft SQL以及Oracle等。 2011-9-17 40  对于读者来说只要能认识到:ADO是微软提供的可以访问数据库 的一种接口方式就可以了。  基于ADO的应用程序可以是单层或多层的,其情况取决于使用的 数据库系统。例如,使用ADO访问MicrosoftSQLServer的程序总 是两层的,因为MicrosoftSQLServer是一个远程数据库系统,数 据库系统通常存放在一个远程专用的SQL服务器上。另一方面, 使用ADO访问本地数据库(如dBASE或FoxPro)的程序,则总是 单层的。  在ADO的应用程序中,数据库是由ADO数据存储(ADO data stores)连接访问的。所以要访问数据库,程序必须首先连接到数 据存储。可以用ADO数据集控件,也可共享由TADOConnection 控件建立的连接来访河到数据存储。  一旦应用程序连接到数据存储,数据集控件就可以与ADO连接控 件关联,并由此访问数据库中的数据表。 2011-9-17 41  基于ADO的应用程序使用Microsoft ADO2.1(或更高版本)和 OLEDBprovider与数据库连接访问和更新存储。ADO2.1(或更高 版本)需要安装在客户端上。ADO和OLEDB由Microsoft公司安装 在Windows中提供。OLEDB连接到数据仓库并对数据进行管 理。  OLEDB provider可以是从本地的OLEDB驱动到ODBC驱动的多 种类型,这些驱动程序必须安装在客户端上。不同数据库系统的 OLE DB驱动程序由数据库系统供应商提供或第三方厂家提供。  有些应用程序使用的数据库需要另外的客户端软件支持,如 Microsoft SQL Server或Oracle,则这些软件也必须安装在客户 端。客户端软件由数据库系统供应商提供。  5.2.2 连接控制  ADO应用程序常用的方法是使用TADOConnection控件建立与数 据库的连接,其它ADO数据集控件和命令控件指向它用以共享该 连接 。除此之外,TADOConnection控件还提供了属性和方法来 打开和关闭连接,控制连接的动作以及直接访问ADO连接对象。 2011-9-17 42  第二种方法是为程序中的每一个ADO数据集控件和命令控件直接建立与数据 库的连接。用第一种方法可以使多个控件共用一个连接,从而便于对连接提 供更多更强大的控制,如进行超时控制,事务处理等。所以在有多个ADO控 件的情况下,最好使用TADOConnection控件建立连接。  1.使用TADOConnection控件建立ADO连接  TADOConnection控件有一个ConnectionString属性,这个属性是多个字符串 的集合,之间用分号隔开,它指明了一个ADO数据库连接。指定这个属性就 建立了一个ADO连接。  指定ConnectionString属性最简便的方法是使用连接字符串编辑器,点击对象 观察器中ConnectionString属性框右边的有省略号按钮将打开如图5-3所示的 对话框。  图5-3 设置连接字符串 2011-9-17 43  这个对话框提供了两种方式指定ConnectionString属性,第一种是使用已有的 数据链接文件,数据链接文件其实就是存放着连接字符串的扩展名为.udl的文 件;第二种方法是使用一个新的连接字符串。下面介绍一下如何创建连接字 符串。  ①单击Build按钮,打开数据连接属性编辑框,如图5-4所示。  ②在“提供者”选项页中列出了所有的OLE DB提供者,用户可根据要连接的数 据库类型选择合适的OLEDB提供者。选择不同的OLEDB提供者,“连接”选项 页的样式,选项的设置也会不同。   选择“使用数据源名称”单选框,在下面的下拉框中选择一种数据库类型;如果 选择“使用连接字符串”单选框,就要在编辑框内输入连接字符串,可以从udl 文件中读取。  在“用户名称”和“密码”框内输入登录数据库时的验证信息,如果此处不设置, 程序登录数据库时也会提示输入信息。  在“输入要使用的初始目录”编辑框内给出数据库所在的目录。 2011-9-17 44  图5-4 选择OLE DB提供者 图5-5 选择数据源 2011-9-17 45  为确保连接有效,可以单击【测试连接】按钮,系统将报告连接 测试成功或失败的信息。  ④ 如果需要,还可以在“高级”选项页中对连接超时和访问权限进 行设置。  ⑤ “所有”选项页显示了此连接的所有属性设置,可以在其中进行 编辑修改。  ⑥ 单击【确定】按钮,关闭连接属性编辑框,回到图5-3的对话 框。  可以看到设置连接字符串的编辑框里已经有相应的连接字符串; 单击OK按钮,结束连接字符串的设置。  当然,也可以在运行期向TADOConnection 控件的 ConnectionString属性赋值,如下所示:  Provider=MSDASQL.1;Persist Security Info=False;User ID=sa;Data Source=dBASE Files  2.ADO连接的常用属性和使用方法  有时,可以将TADOConnection控件的ConnectionObject属性直 接指向一个已有的ADO连接,用来代替自建连接。不过,这要求 用户对ADO与ADO连接非常熟悉,所以一般不推荐使用这种方 法。 2011-9-17 46  此外,如果TADOConnection有与之相连的ADO数据集控件和命 令控件的话,当数据集控件被激活时,或命令控件中的命令被执 行时,TADOConnection控件也将被激活。  激活TADOConnection 控件时将触发OnWillConnect 事件和 OnConnectComplete事件。用户可编写这两个事件句柄的处理 代码,来完成特定的功能。  反过来,把Connected属性设为False或调用Close方法将关闭一 个TADOConnection控件。此时,将首先触发OnDisconnect事 件,接着关闭TADOConnection控件,最后关闭与之相连的ADO 数据集控件和命令控件。  在程序运行期,可以通过访问TADOConnection控件的State属性 判别控件所处的状态。当State值为stClosed时,表示控件处于关 闭状态:State值为stOpen表示控件为打开状态;State值为 stConnecting表示控件正在建立连接字符串设定的连接,此时可 以用Cancel方法取消建立连接。 2011-9-17 47 5.3客户端与服务器的连接  在建立执行SQL命令前,应用程序必须先和数据库建立连接。  5.3.1 SLMIS系统登录窗体的设计  对多用户数据库程序来说,不同的用户有不同的权限,比如管理员可以无任 何限制、有的用户只有查询的权限没有修改的权限、有的用户只可以录入数 据但不能查询等等。所以在数据库程序启动时,需要输入用户名和密码后才 能进入,程序根据用户名找到用户的权限确定哪些功能可以使用。SLMIS系 统登录界面如图5-6所示。  登录窗口的设计可以是多种多样的,但一般应有用户名输入和密码输入两个 文本框。考虑到使用该系统的用户需要使用真实姓名,这样输入用户名时比 较麻烦,登录窗口启动后自动将所有用户名列出;同时,该系统的用户很 多,于是又添加了部门列表框,这样用户就可以首先选择所在部门,此时用 户名下拉列表框就只列出所有该部门的用户名。  对于用户修改自己的密码和管理员管理用户这个功能,目前大部分数据库程 序都放在了主窗口的菜单中,优点是用户可随时修改自己的密码,管理员可 随时管理用户。 2011-9-17 48  在设计登录窗体之前,首先要保证数据库中要有一个用户数据表,数据表的 记录应该至少有这么几个字段:用户名、密码、权限,其中密码最好是加密 存放的。因为该系统较为庞大,可能有多个用户具有相同的权限,多个用户 同属于一个部门,同时该部门可能更名等,于是在SLMIS中一共使用了三个 基础表来完成这基于角色一功能。  图5-6 登录窗口 图5-7 数据库连接初始化  除此以外,系统登录前必须与服务器上的数据库连接起来,因此还要检查对 应的连接是否已设置完毕。在SLMIS中,如果在注册表中找不到相应的设 置,应首先弹出注册窗体让用户填写服务器数据库设置信息,界面如图5-7所 示。 2011-9-17 49  5.3.2设置用户权限  由于SLMIS功能模块很多,每一个角色可能有任意功能的组合,系统采用0、 1这种方式表示,字段内容由多个0和1组成的字符串构成,1代表有该项权 限,0代表没有该项权限,从第一个字符开始对应相应的功能,属于某一个角 色的用户就继承该角色的所有权限,这样程序就可判断出用户的权限了。  设置每一个角色的权限界面如图5-8所示。  图5-8 设置角色的权限图 5-9 添加用户 2011-9-17 50  5.3.3 SLMIS系统登录功能的实现代码  系统登录功能共分为两个部分,即系统登录和系统注册。  1.系统登录功能  procedure TF_Login.Button1Click(Sender: TObject);  var  passwd:string; rightid:integer;  begin  username:=ComboBox1.Text;  passwd:=edit1.text;  Edit1.Text:='';  if (username<>'')and(passwd<>'') then  begin  ADOQuery2.Close;  ADOQuery2.SQL.Clear;  ADOQuery2.SQL.Add('select * from USE_USER WHERE C_NAME=:username AND C_PASS=:passwd');  ADOQuery2.Parameters.parambyname('username').datatype:=ftstring;  ADOQuery2.Parameters.ParamByName('username').Value:=username;  ADOQuery2.Parameters.parambyname('passwd').datatype:=ftstring;  ADOQuery2.Parameters.ParamByName('passwd').Value:=passwd;  ADOQuery2.Open;  if NOT ADOQuery2.IsEmpty then  begin 2011-9-17 51  ADOQuery2.First;  rightid:=ADOQuery2.FieldValues['I_ROLEID'];  ADOQuery1.Close;  ADOQuery1.SQL.Clear;  ADOQuery1.SQL.Add('select * from USE_ROLE WHERE I_ID=:rightid');  ADOQuery1.Parameters.parambyname('rightid').datatype:=ftinteger;  ADOQuery1.Parameters.ParamByName('rightid').Value:=rightid;  ADOQuery1.Open;  if NOT ADOQuery1.IsEmpty then  begin  ADOQuery1.First;  if (ADOQuery2.FieldValues['I_PASSED']=I_Co1)  OR (ADOQuery2.FieldValues['I_PASSED']=I_Co2) then  begin  userright:=ADOQuery1.FieldValues['C_POPEDOM'];  userrole:=ADOQuery1.FieldValues['C_NAME'];  F_Login.Hide;  systemflagid:=WriteLogin(); //填写登录信息  dfm11.Show;  end 2011-9-17 52  else  begin  ComboBox1.SetFocus;  exit;  end;  end;  end  else  begin  showmessage('密码错误!');  edit1.Text:='';  ComboBox1.SetFocus;  exit;  end;  end  else  begin  showmessage('用户名或密码为空!');  edit1.SetFocus;  exit;  end;  end; 2011-9-17 53  procedure TF_Login.ComboBox2Select(Sender: TObject);  var  depid:integer;  name1:string;  begin  Edit1.Text:='';  name1:=trim(ComboBox2.Text);  ComboBox1.Items.Clear;  ADOQuery2.Close;  ADOQuery2.SQL.Clear;  ADOQuery2.SQL.Add('select * from USE_DEP WHERE C_DEPARTMENT=:name');  ADOQuery2.Parameters.parambyname('name').datatype:=ftstring;  ADOQuery2.Parameters.ParamByName('name').Value:=name1;  ADOQuery2.Open;  if not ADOQuery2.IsEmpty then  begin  ADOQuery2.First;  depid:=ADOQuery2.FieldValues['I_ID'];  ADOQuery2.Close; 2011-9-17 54  rightid:=ADOQuery2.FieldValues['I_ROLEID'];  ADOQuery1.Close;  DOQuery1.SQL.Clear;  ADOQuery1.SQL.Add('select * from USE_ROLE WHERE I_ID=:rightid');  ADOQuery1.Parameters.parambyname('rightid').datatype:=ftinteger;  ADOQuery1.Parameters.ParamByName('rightid').Value:=rightid;  ADOQuery1.Open;  if NOT ADOQuery1.IsEmpty then  begin  ADOQuery1.First;  if (ADOQuery2.FieldValues['I_PASSED']=I_Co1)  OR (ADOQuery2.FieldValues['I_PASSED']=I_Co2) then  begin  userright:=ADOQuery1.FieldValues['C_POPEDOM'];  userrole:=ADOQuery1.FieldValues['C_NAME'];  F_Login.Hide;  systemflagid:=WriteLogin(); //填写登录信息  dfm11.Show;  end 2011-9-17 55  else  begin  ComboBox1.SetFocus;  exit;  end;  end;  end  else  begin  showmessage('密码错误!');  edit1.Text:='';  ComboBox1.SetFocus;  exit;  end;  end  else  begin  showmessage('用户名或密码为空!');  edit1.SetFocus;  exit;  end;  end; 2011-9-17 56  procedure TF_Login.ComboBox2Select(Sender: TObject);  var  depid:integer;  name1:string;  begin  Edit1.Text:='';  name1:=trim(ComboBox2.Text);  ComboBox1.Items.Clear;  ADOQuery2.Close;  ADOQuery2.SQL.Clear;  ADOQuery2.SQL.Add('select * from USE_DEP WHERE C_DEPARTMENT=:name');  ADOQuery2.Parameters.parambyname('name').datatype:=ftstring;  ADOQuery2.Parameters.ParamByName('name').Value:=name1;  ADOQuery2.Open;  if not ADOQuery2.IsEmpty then  begin  ADOQuery2.First;  depid:=ADOQuery2.FieldValues['I_ID'];  ADOQuery2.Close;  ADOQuery2.SQL.Clear; 2011-9-17 57  ADOQuery2.SQL.Add('select C_NAME,C_PASS from USE_USER WHERE I_DEPART=:depid');  ADOQuery2.Parameters.parambyname('depid').datatype:=ftinteger;  ADOQuery2.Parameters.ParamByName('depid').Value:=depid;  ADOQuery2.Open;  ComboBox1.Items.Clear;  if NOT ADOQuery2.IsEmpty then  begin  ADOQuery2.First;  while not ADOQuery2.Eof do  begin  ComboBox1.Items.Add(ADOQuery2.FieldValues['C_NAME']);  ADOQuery2.Next;  end;  end;  end;  end; 2011-9-17 58  2.系统注册  系统注册功能是检查是否已注册,如果注册表中有服务器数据库信息则连接,如果没 有注册则弹出注册信息窗体要求用户填写服务器数据信息,并将其写入注册表。  procedure TF_Login.FormShow(Sender: TObject);  var reg:TRegistry;  begin  reg:=tregistry.Create;  reg.RootKey:= HKEY_CURRENT_USER;  if reg.OpenKey('\software\Ciedwhpu',false) then  begin  ADOQuery1.CLOSE;  ADOQuery1.SQL.Clear;  ADOQuery1.SQL.Add('select C_DEPARTMENT from USE_DEP');  try  ADOQuery1.Open;  except  F_Login.Release;  end;  ComboBox2.Items.Clear;  if NOT ADOQuery1.IsEmpty then  begin 2011-9-17 59  ADOQuery1.First;  while not ADOQuery1.Eof do  begin  ComboBox2.Items.Add(ADOQuery1.FieldValues['C_DEPARTMENT']);  ADOQuery1.Next;  end;  end;  ComboBox1.Items.Clear;  ADOQuery2.CLOSE;  ADOQuery2.SQL.Clear;  ADOQuery2.SQL.Add('select C_NAME from USE_USER');  ADOQuery2.Open;  if NOT ADOQuery2.IsEmpty then  begin  ADOQuery2.First;  while not ADOQuery2.Eof do  begin  ComboBox1.Items.Add(ADOQuery2.FieldValues['C_NAME']);  ADOQuery2.Next;  end; 2011-9-17 60  end;  end  else  begin  reg.CloseKey;  end;  reg.Free;  end;  procedure TF_Login.FormCreate(Sender: TObject);  var constr,Red:string;  reg:TRegistry;  begin  reg:=tregistry.Create;  reg.RootKey:= HKEY_CURRENT_USER;  if reg.OpenKey('\software\Ciedwhpu',false) then  begin  constr:='Provider=SQLOLEDB.1;Password='+reg.ReadString('password'); //口令  constr:=constr+';Persist Security Info=false;  User ID='+reg.ReadString('username');//用户名  constr:=constr+';Initial Catalog='+reg.ReadString('database');//数据库  constr:=constr+';Data Source='+reg.ReadString('computer');//服务器  Red:=reg.ReadString('registed');//是否已注册  2011-9-17 61  DataModule1.ADOConnection1.ConnectionString:=constr;  try  DataModule1.ADOConnection1.Open;  Button3.Visible:=false;  except  on ELoginFailed do  begin  Button3.Visible:=true;  Timer1.Enabled:=true;  showmessage('数据库连接失败,请检查网络是否连通!');  application.Destroy;  end;  end;  end  else  begin  Timer1.Enabled:=true;  end;  end; 2011-9-17 62  procedure TF_Login.Timer1Timer(Sender: TObject);  begin  F_Login.Hide;  Application.CreateForm(TFrmRegister, FrmRegister);  FrmRegister.Show;  Timer1.Enabled:=false;  end;  end. 2011-9-17 63 5.4小结 本章在Dephi访问数据库的数据集控件体系的前 提下主要介绍了数据集控件的常用属性和方法以 及ADO方式的连接控制方法,并在基于实例的基 础上对C/S模式下的动态设置连接和用户登录、 权限等方面做了介绍。重点要求掌握好如何在程 序执行过程(主要初始化过程中)如何设置与数 据库的ADO连接。 2011-9-17 64 5.5习题5  1.Dephi提供了哪些数据访问机制。  2.使用ADO连接数据库的方式中Dephi主要使用那些 数据集控件,其主要常用属性包括哪些。  3.数据集的事件有哪些,分别在何时被触发。  4.如何使用TADOConnection控件的属性和方法来打 开和关闭与数据库的连接。  5.在基于C/S模式的环境下,可以动态设置数据库的连 接吗?如果可以,该如何设置。 
还剩63页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 6 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

lyon11

贡献于2012-11-30

下载需要 6 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf