delphi 类的写法 和 控制台程序的制作---深入Delphi编程 数据库

2、数据库
  
  在相对枯燥的理论之后,我们终于要开始接触到一些比较激动人心的实际应用了。
  数据库编程是Delphi最强大的优势之一,恐怕也很少有Delphi程序员没有接触过数据库编程的。Delphi独特的Data-Aware构件,让很多初识Delphi的人为之目瞪口呆。不需要写任何代码,在几分钟之内就可以做出一个相当精巧的数据库程序,而且在开发期就可以看到运行期的结果,这真是不可思议啊!但是,Delphi强大无比的数据库开发能力,决不仅仅限于用几个构件操纵一下DBF或是Access数据库而已。你所看到只是冰山一角。让我们仔细说来。
  数据库虽然家族庞大,但一般来说可以分为两种:文件型数据库和C/S型数据库。下面分别讨论。
  
  2.1 文件型数据库
  所谓文件型数据库,顾名思义,是基于文件的(file-based),数据被按照一定格式储存在磁盘里,使用时由应用程序通过相应的驱动程序甚至直接对数据文件进行读取 。也就是说,这种数据库的访问方式是被动式的,只要了解其文件格式,任何程序都可以直接读取,这样就使得它的安全性相当糟糕。同时,在蓬勃兴起的网络应用,文件型数据库更是难有用武之地:效率低下,不支持很多SQL命令,不支持视图、触发器、存储过程等高级功能,等等。这些特点决定了它不适合大型的工程。
  最为大家所熟悉的文件型数据库可能就是DBF(DBase/Foxbase/Foxpro)数据库,在DOS时代风靡一时,相信很多人都有过抱着一本手册苦背Foxbase命令的回忆吧!其特点是,每个Table或Index都是一个独立的文件,使用相当简单,性能还可以,安全性非常的差,但应用非常广泛(主要是DOS时代遗留下来的,哪个单位没有两个用这种东东编出来的老古董程序呢?)。它在今天还能占有一席之地,其主要原因之一是,正因为简单和使用广泛,使得对它的访问是最容易的,甚至根本无需第三方的接口,就可直接对其进行字节级的读取 。
  除此之外,还有著名的Access数据库。这是MS Office里的构件之一,和DBF数据库不同,所有的文件都被整合在一个.mdb文件中,这样就避免了数据库变大之后管理上带来的麻烦。同时它还提供密码保护功能,安全性比DBF数据库要好很多。Access数据库除了一般的文本数据之外,还擅长于对多媒体数据的处理,在对声音、图像乃至基于OLE的对象进行处理时,令DBF数据库望尘莫及。随着微软战略的步步胜利,Access数据库也不断发展,凭借着优秀的性能和与MS Office的无缝结合,早已超越DBase系列,成为现今最强大的文件型数据库了。
  Delphi中附带的Paradox也是一种文件型数据库。它是Inprise公司自己的产品。因此和Inprise的系列开发工具配合得很不错。它支持密码保护,支持标准的SQL,性能也还不错,但是应用就不那么广泛了。和DBF数据库一样,它的每一个Table都是一个独立的文件,因此也有同样的管理问题。
  上文说到可以对文件型数据库直接读取,但实际编程中很少有人这么做。因为再简单的数据库其实也是相当复杂的,一步步分析它的格式,从底层实现所有的数据库应用,如果都要程序员去写的话,可能会把人累死。所以数据库的开发商将这些访问代码封装起来,向程序员开放,程序员只需要调用相应的接口就可以了。
  以DBF为例,使用DBase/Foxbase/Foxpro系列开发工具,可以用它自己的语法开发出应用程序。其中对DBF文件的具体操作被封装了。对于Access数据库,微软公布了一个DAO(Database Access Object),由一系列的DLL文件组成,封装了对.mdb文件的访问。使用VB的读者可能对DAO比较熟悉,只要在VB中嵌入DAO对象,就可以非常方便地访问Access数据库了。ODBC(Open DataBase Connection,开放数据库互连)也是一种封装,用意在于向开发人员提供一个统一的接口,通过这个接口可以访问任何支持ODBC的数据库,只要该数据库提供了相应的ODBC驱动。从这一点上来说,ODBC是一种更加高级的封装。目前几乎所有的主流的数据库都能被ODBC所支持。打开你的Windows的控制面板,就可以看到ODBC的图标。
  用Delphi写数据库程序的人免不了要同BDE打交道。BDE(Borland Dasebase Engine,Borland数据库引擎)是一个和ODBC类似的东西,Borland/Inprise本来企图用它来统一数据库接口。但后来Inprise在和微软的战争中败下阵来(ODBC是微软搞出来的),它又不肯放弃BDE,而是将其捆绑在Delphi/C++ Builder系列开发工具中,结果好象变成这些开发工具的一种附属品了。
  用BDE开发数据库程序相当容易。许多Delphi教科书在写到数据库开发这一章时,总是告诉你先在BDE中为某个DBF或Paradox数据库设置一个别名,然后往窗体上放一个TTable构件,然后将其DatabaseName指向相应的别名……然后,这个数据库中某个表的内容就在相应的Data-Aware构件中显示出来了。但是它们具体是怎么工作的呢?
  Delphi对数据库进行访问时,事实上通过了很多层次的连接。如下图:
  
  图1
  DataAware构件-DataSource构件-DataSet构件-BDE-数据库
  
  从这个图可以看出,BDE负责与具体的数据库打交道,而Dataset构件与BDE相连,DataSource构件与Dataset构件相连,最后才连接到显示具体数据的Data-Aware构件。在Delphi的构件面板上,Data Access页面中的构件一般属于DataSet构件,例如TTable、TQuery,只要指定它们的DatabaseName属性,就可以将它们与某个数据库建立连接。在Data Control页面中的构件一般是Data-Aware构件,例如TDBGrid,TDBEdit,TDBImage。它们的作用看上去与一般的Delphi构件相似,不同之处在于,可以通过一个DataSource构件作为中介,与DataSet构件相连,并自动显示相应的数据。
  用Delphi的数据库构件建立一个应用程序是如此之方便,但是如果深入下去,会发现事情并不简单。你可以尝试自己编写代码,访问数据库中字段,而不是通过Data-Aware构件由用户来编辑。如何做到这一点呢?秘密在于Field构件。
  可以说,Field构件是Delphi数据库应用程序的基础 。当打开一个DataSet构件时,相应的数据会被读取,并储存在TTable或TQuery构件的Fields属性中。这个属性被定义为Field数组。通过直接访问数组,可以使用它们,例如:
  
  Table1.Fields[0].AsInteger;
  
  这段代码访问了Table1中当前记录的第一个字段,该字段的类型为Integer。
  也可以通过使用FieldbyName属性来使用它们:
  
  Table1.FieldbyName('Last Name').AsString;
  
  这段代码访问了Table1中当前记录的名为Last Name的字段,该字段的类型为String。
  事实上,Data-Aware构件就是通过访问DataSet构件的Fields属性来使用数据的。弄明白了这一点之后,你自己也可以尝试改写一个常规的显示构件,使之具有Data-Aware的性质。其实,大多数使用Delphi的数据库高手并不喜欢使用Data-Aware构件,因为Data-Aware构件远不用常规的构件来得灵活。DataSet构件除了Fields属性之外,还具有数目众多的特殊属性、方法和事件,足以应付从小型文本数据库到大型网络数据库的所有应用。本文不拟一一讨论它们,如果读者能将它们的运用烂熟于心的话,可以说应付数据库编程就不会有多大问题了。
  请将注意力再次集中到图1。在图1的最后一环,可以看到BDE连接到了具体的数据库。其实,在这一环中,也是有几个层次的。理论上来说,BDE可以连接任何类型的数据库。对于一些比较简单的数据库,例如ASCII(纯文本型的数据库)、dBase以及Delphi自己的Paradox,BDE可以直接访问。另外它也可以通过一些相应的驱动,访问特定的数据库,例如通过DAO访问Access数据库。对于不能直接支持的数据库,BDE还可以连接到ODBC,通过ODBC进行访问,虽然这样效率比较低。
  这种性质决定了BDE是一个相当庞大的东西。使用了BDE的Delphi程序,必须有BDE才能工作,所以必须同BDE一起发布。这样往往造成这样一种情况:只有几百K的应用程序,在将整个BDE加入之后,体积将近10M!这对于以轻薄短小为长的文件型数据库,简直是一个致命的弱点。而且由于BDE要兼容太多的数据库,本身也有不稳定的毛病,往往出现令人头疼的问题。同时,通过安装程序安装BDE驱动和设置数据库别名也是一件很麻烦的事情,这一切使得BDE在Delphi程序员中很不受欢迎。在网上的Delphi技术论坛里,经常可以看到对BDE的一片咒骂之声……那么,有什么办法可以绕过BDE吗?
  有的。目前来说,至少有以下三种方法:
  (1) 使用第三方构件。
  Inprise自己也很早就意识到了BDE的问题,虽然他们不肯放弃BDE,但是从Delphi3起,仍然对程序员提供了一个不错的选择:创建自定义的DataSet构件。Delphi的开发者们把所有有关BDE的东西从TDataSet类中移走,放入了新的TBDEDataSet类(TBDEDataSet类是TDataSet类的子类)。TDataSet类被重新构造,其核心功能被虚拟化。因此,你只需要从TDataSet类派生一个自己的新类,并重载一些指定的虚拟方法(用以访问具体的数据库),你就可以得到一个自己的DataSet构件。它与BDE完全无关,但可以象Delphi自己的DataSet构件一样被使用,例如,访问其Fields属性,乃至与Delphi的Data-Aware构件一起工作!
  于是出现了大量的第三方构件,它们可以访问某种特定的数据库。下面是一些比较常见的访问文件型数据库或ODBC的第三方构件:
  
  
  表2

名称 支持的数据库类型
Diamond Access
Halcyon DBase/Foxpro
Apollo DBase/Foxpro
mODBC 任何ODBC数据库
ODBC Express 任何ODBC数据库




  
  这些控件被广泛使用,在国内,就作者所知,财智家庭理财软件使用了Diamond,而“追捕”(一个显示指定IP的地址位置的共享软件)使用了Halcyon。在使用这些第三方构件之后,软件终于可以“轻装上阵”,再也不用为BDE头疼了。
  (2) 使用ADO。
  在Delphi5中,Inprise终于提供了一个比较彻底的解决方法,那就是ADO构件。从原理上来说,ADO与上述的第三方构件并无多大区别,只是它是Inprise官方开发的;同时,它连接的不是某个具体的数据库,而是微软提供的ADO对象。
  ADO(ActiveX Data Object,ActiveX数据对象)是微软提出的新标准,从理论上来,能够支持任何类型的数据库(甚至包括流式数据)。微软力图将它树为新的统一数据库接口,吹嘘了它的许多优点。Inprise一直是微软不共戴天的竞争对手,对微软的标准嗤之以鼻(BDE即是一例),但是由于种种原因,Inprise终于承认了ADO。平心而论,用ADO来取代BDE的确是一个不错的解决方案,而且在Delphi中使用ADO也相当方便。从形势看,ADO应该是未来的方向。但是,ADO本身也是相当大的。
  (3) 从最底层开发一个完整的数据库引擎。
  这是最彻底的办法。彻底抛弃Delphi的数据库支持,从字节开始,开发自己的数据库。这种方法有其好处:第一,不用考虑兼容性问题,例如不用去考虑用户的数据库文件是Access 97格式还是Access 2000格式的;第二,可以在性能上达到最充分的优化,因为不需要通过任何通用接口,而是直接对磁盘文件进行操作,这对于一些对性能要求苛刻的程序是很有用的;第三,能够最大限度地减少冗余代码,因为这种数据库往往是特定格式的,而且只需要执行一些特定的操作,访问代码当然要比通用数据库精简得多。但这种方法的负面问题也显而易见,那就是庞大的工作量。再简单的数据库也是相当复杂的,从最底层实现一个完整的数据库引擎,往往需要几千行代码,以及耐心和经验。
  虽然听起来有些极端,但这样做的也不乏其人。著名的Foxmail就是使用了自定义的数据库格式来储存信件、地址本等有关信息。另一个共享软件“电子书库”也使用了自定义的.srm格式。作者开发的iCompanion(网络伴侣)也是使用自定义格式来储存网络记录的。
  限于篇幅,这里就不再对具体的程序进行详细的分析了。要补充的一点是,作者曾使用Diamond开发过Rich Explorer,这是一个专门用于浏览著名的大富翁论坛的离线数据库(Access格式)的阅读器。在作者的主页上,可以找到Rich Explorer的全部源代码,它完整地展示了一个使用第三方构件访问特定数据库的程序(没有使用Data-Aware控件),代码也比较简单,适合于初学者分析,有心的读者不妨作为参考。
  
  2.2 C/S型数据库
  C/S(Client/Server,客户机/服务器)型数据库是当前数据库应用的主流。
  与文件型数据库不同的是,C/S型数据库应用程序由两个部分组成:服务器和客户机。服务器指数据库管理系统(Database Manage System,DBMS),用于描述、管理和维护数据库的程序系统,是数据库系统核心组成部分,对数据库进行统一的管理和控制。客户机则将用户的需求送交到服务器,再从服务器返回数据给用户。
  C/S型数据库非常适合于网络应用,可以同时被多个用户所访问,并赋予不同的用户以不同的安全权限。C/S型数据库支持的数据量一般比文件型数据库大得多,还支持分布式的数据库(即同一数据库的数据库位于多台服务器上)。同时,C/S型数据库一般都能完善地支持SQL语言(所以也被称作SQL数据库)。这些特性决定了C/S型数据库适合于高端应用。
  常见的C/S型数据库有著名的Oracle, Sybase, Informix, 微软的Microsoft SQL
  server, IEM的DB2,以及Delphi自带的InterBase,等等。
  C/S型数据库涉及到非常多的高级特性,是Delphi中,也是整个计算机领域中最大的应用之一。由于本期附录中已有专文讨论,本文就不拟详细介绍了。
  随着技术的不断更新,C/S型的结构也开始逐渐被多层(Multi-Tiered)数据库模型所取代。
  上面说到,C/S型数据库程序由服务器和客户机两个部分组成,因此被称为双层(two-tiered)模型。文件型数据库程序则被称为单层(single-tiered)模型。单层模型是最原始的数据库模型。后来程序员们将数据库转移到一个强大的中央服务器上,让它为多个功能较弱的客户机提供服务,这样双层模型出现了。双层模型在金融、电力、通信等领域被广泛使用,极大地推动了网络数据库的发展。但是,双层模型也逐渐暴露出其不足的一面。在这种情况下,出现了三层模型:应用程序中的数据模块部分被分离出来,转移到一个单独的服务器上,成为独立的一层。三层和三层以上的模型,统称为多层模型。
  ` 简言之,三层模型由以下三个层次组成:
  
  客户机-应用程序服务器-数据库服务器
  
  用户的请求首先通过客户机向应用程序服务器发出,应用程序服务器再向数据库服务器发出具体的数据访问命令(一般是SQL),数据库服务器返回的数据被应用程序服务器重新组织之后返回给客户机。
  可以看出,三层模型是双层模型的扩展。目前我们无需了解三层模型的所有技术细节,以及它对于双层模型的优势,只需要大致理解这个模型的结构就可以了。
  B/S模型无疑是当前最为流行的多层数据库模型之一。也许你已经听说过B/S这个名词,它是Brower/Server(浏览器/服务器)的缩写。Brower是指IE/Netscape这样的浏览器,Server包括数据库服务器和应用程序服务器。用户通过浏览器发出某个请求,通过应用程序服务器-数据库服务器之间一系列复杂的操作之后,返回相应的Html页面给浏览器。
  是不是觉得十分熟悉?对了,其实这就是大家再熟悉不过的Internet上的WEB数据库,当然它也可以用于局域网。它实际上可以说是一种最常见的多层模型。
  在对数据库的发展进行回顾之后,我们终于赶上了最流行的步伐。但是,也许有人在这里却感到泄气了。因为他听说现在的WEB数据库编程,是Perl、ASP、PHP、JAVA这些语言的天下。难道我们一直忠实追随的Delphi,在面对当代最流行的Web数据库的时候,竟然面临淘汰的命运?
  不,不是的。其实Delphi对Web数据库的开发提供了非常良好的支持,特别是依据强大的数据库支持和可视化编视的特点,它有着其他语言不可比拟的优势。
  下面的内容将集中于用Delphi开发Web数据库。
  首先要从Web本身讲起。平时我们浏览的Web页面,一般可以分为两种。一种是静态页面,这种页面是文本格式的html文件。但是,要响应用户的不同需求,从而反馈给用户不同的页面,就必须使用动态页面技术了。例如,根据用户输入的名字,迅速在数据库中查找到相应的数据,并动态生成一个页面返回给用户。
  怎样实现动态页面技术呢?最早的一种方法是CGI(Common Gateway Interface,通用网关接口)。这种接口允许浏览器发送和接收数据,并且基于应用程序的命令行进行输入和输出。当浏览器发出指向应用程序的请求时,Web服务器会自动启动该程序,并在浏览器和应用程序之间传递输入和输出的数据。实现CGI的语言有很多种,其中比较流行的是Perl。另外,还有一种特殊的WinCGI。WinCGI与普通CGI的区别是,它通过INI文件来代替命令行参数作为输入输出,这主要是为Visual Basic设计的,因为VB不能访问环境变量。Delphi对这两种CGI都提供了很好的支持,可以编写出非常复杂的CGI程序。
  第二种方法是使用Web服务器内置的API接口。用这种接口编写出来的Dll文件,被Web服务器装载到自己的内存空间中,当服务器接收到相应的页面请求时,它将启动一个新的线程来执行Dll中的相应代码。由于不需要执行外部的EXE文件,这种程序的速度非常快。这种API主要有ISAPI(Internet Server API)和NSAPI(Netscape Server API),其中前者已经成为事实上的标准。Delphi对这两种API也提供了很好的支持。提得一提的是,Delphi的C/S版提供了一个通用的类框架,消除了CGI、WinCGI和ISAPI之间的区别,这样,我们可以轻松地将一个应用程序在这三者之间转换。
  还有一种是Web服务器内置的脚本语言,可以被简单地嵌入html文件,通过Web服务器的解释执行来产生动态页面。著名的ASP、PHP、JSP都属于此类。这看起来和Delphi没有什么关系,但事实上,Delphi也能提供对ASP的强力支持!ASP的初学者可能会把注意力都集中在它的脚本语言上,其实ASP是由三部分组成的:脚本、组件和html。光是掌握脚本和ASP原有的组件特性是远远不够的,只有自行开发组件,才能实现真正复杂、高效的应用。Delphi就非常适合开发这种组件。国内著名大富翁论坛,就是基于Delphi+ASP的成功例子,同时它还公布了所有的源代码,可供借鉴。
  让我们先建立一个程序。这个程序和以往的Delphi不同,要求删除所有的窗体和多余的代码,最后只剩下这么一段:
  
  program CgiDate;
  
  {$APPTYPE CONSOLE} //这行编译器指令表示这是一个控制台程序,不需要窗体,在终端窗口中输出
  
  uses
  sysutils;
  
  begin
  writeln('HTTP/1.0 200 OK');
  writeln('CONTENT-TYPE: TEXT/HTML');
  writeln;
  writeln('<html> <head>');
  writeln(' <title>Time</title>');
  writeln('</head><body>');
  writeln('<h1>Time at this site</h1>');
  writeln('<hr>');
  writeln('<h3>');
  writeln(formatdatatime('"Today is " dddd,mmmm d,yyyy,' + '"<br> and the time is "hh:mm:ss AM/PM',now));
  writeln('<h3>');
  writeln('<hr>');
  writeln('</body></html>');
  end;
  
  编译后,将该程序置于Web服务器下的有scripts权限的子目录中,并用浏览器访问它,就可以得到一个关于当前时间和日期的页面。
  分析一下这段代码。格式很简单,都是用writeln生成标准输出。首先的两行生成html的描述,中间空一行,接下来的代码生成了一个完整的html文件。这个文件被返回给浏览器并显示出来。与静态页面不同是,有一行html文本是通过formatdatatime函数动态生成的,因此根据当前时间的不同会有不同的显示。
  这就是一个最简单的CGI程序。虽然简单,但已经可以看出CGI程序的基本工作原理:通过标准输入输出产生动态的html页面。
  在这个基础之上,可以方便地实现对数据库的访问,并生成相应的html。
  下面是一个例子:
  
  var:
  Table1:TTable;
  Begin
  Showheader; // Showheader过程生成html文件的头部,代码与上例相似
  Table1 := TTable.create(nil);
  Try
  Table1.Databasename := 'DBDEMOS';
  Table1.tablename := 'Employee.db';
  Table1.indexname := 'Byname';
  Table1.open;
  ShowTabindex; //显示表中的列
  Finally
  Table1.close;
  Table1.free;
  End;
  Writeln('</body></html>');
  End;
  
  这段代码动态建立了一个Table对象,并将它与DBDEMOS数据库中的表Employee.db相连(当然这些必须先在BDE中设置)。ShowTabindex过程显示了该表中的两个列:
  
  procedure ShowTabindex;
  begin
  table1.frist;
  writeln('<ul>');
  while not table1.eof do
  begin
  writeln(format('<li>s%s%',[table1.fieldbyname('FirstName').AsString, table1.fieldbyname('LastName').AsString]);
  Table1.Next;
  End;
  Writeln('</ul>');
  End;
  
  ShowTabindex函数遍历了整个表,并将所有的FirstName和LastName字段打印出来。
  在此基础之上,可以产生复杂的动态页面,实现对数据库的各种操作。当然,实践中的问题决不仅仅这么简单,例如,读取有关的环境变量,要使用特殊的GetEnvironmentVariable变量,生成相应的URL,需要相应的http知识,产生供用户提交请求的页面,需要了解html表单的格式……但是,本文的目的不在于探讨技术细节,而是着重于基本概念和原理的了解,因此不再详述,请读者自己深入学习。
  上面已经说过,在Delphi中可以轻松地将一个应用程序在CGI、WinCGI和ISAPI之间转换。上面这两个例子生成的是EXE文件,因此是CGI(或WinCGI,取决于使用环境变量还是INI文件)程序。如果要改成ISAPI方式,除了要编译成DLL文件之外,输入输出的部分也需要进行修改,实现三个特殊的函数:
  GetExtensionversion
  TerminateExtension
  HttpExtensionProc
  Web服务器在装载DLL、卸载DLL和请求DLL时会分别调用这三个函数,程序可以通过它们与浏览器进行交互。具体的实现,这里也不详述了。
  
  
  通过以上几个专题的学习,相信读者对Delphi的了解更深了一层。本文旨在帮助读者理解一些相对比较高级的概念和原理,很多地方只是介绍了最基础的东西,没有展开。如果深入下去,将会产生很多章庞大的内容。因此,读者在入门之后,需要更多的自己的努力。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值