Delphi7下用dbExpress调用Oracle存储过程(返回数据集)的一个简单示例和调试过程

作者: jrq
标题: Delphi7下用dbExpress调用Oracle存储过程(返回数据集)的一个简单示例和调试过程
关键字: Delphi7 dbExpress Oracle 存储过程 游标
分类: 成功心得
密级: 公开

摘要:这是2003年曾发布在大富翁论坛上的笔记:

       http://www.delphibbs.com/keylife/iblog_show.asp?xid=4018

今天转到这里一份,作备忘,以方便查阅。

标题: Delphi7下用dbExpress调用Oracle存储过程(返回数据集)的一个简单示例和调试过程

关键字:Delphi7 dbExpress  Oracle  存储过程  游标

作者:jrq

 

链接:http://www.delphibbs.com/keylife/iblog_show.asp?xid=4018

 

正文:

一、问题缘起:    
    昨天晚上在网络上偶遇jcc老兄,谈起一个月前的一个帖子。他是想用dbExpress调用oracle中返回数据集的存储过程,用来制作报表的。

    对我来说,在Delphi中调用Oracle返回数据集的存储过程一直是个头疼的问题,以前曾用ADO调用过,费了很大劲才搞定(多谢大富翁论坛上的朋友热心帮助)。

    当时我在那个帖子中插了一句话,又在QQ上和他聊了很多。后来在D6下做了个测试,用dbExpress调用oracle返回数据集的存储过程。尝试参考BDE的和ADO的调用的方法,调试了很长时间,都没有成功。再后来这个事就扔起来了,悬而未决。

    昨天晚上恰又逢jcc,谈到了一个月前的这个问题,他说“后来总算七碰八碰的把问题解决了,嘿嘿”。我听后,很是感兴趣,所以就做了一个DEMO来测试。

   jcc的帖子链接如下:

   大富翁的帖子:
      http://www.delphibbs.com/delphibbs/dispq.asp?lid=2196755

   CSDN的帖子:
      http://expert.csdn.net/Expert/topic/2302/2302711.xml?temp=3.541201E-02
      http://expert.csdn.net/Expert/topic/2358/2358613.xml?temp=.748852
      http://expert.csdn.net/Expert/topic/2353/2353495.xml?temp=.8681757

    看帖子,是用的D6。于是在Delphi6中按照帖子所说的方法,将游标定义在包内,存储过程写在包外,用TSQLConnection连数据库,然后用TSQLStoredProc连存储过程,发现还是无法正常执行,出现错误提示,直到现在也没有搞明白问题所在。

    接下来又搞了一个多小时,用PL/SQL写了一些简单的存储过程(带传入参数的和不带传入参数的)做测试,均调用不成功。需要和jcc做进一步交流,他正在加班,给我发来一句“现在忙!一定不要打挠,以后时间多的是给你!”就忙去了。
    叹了口气,看来我只好自力更生了。(写到这里想起了三五九旅和南泥湾,肃然起敬! ^ō^)

   在整个的用D6调试的过程中,出现的错误提示,我见到的大致有以下几种情况:
   ---------------------------
   ORA-06550: 第 1 行, 第 7 列:
   PLS-00201: 必须说明标识符
   ORA-06550: 第 1 行, 第 7 列:
   PL/SQL: Statement ignored.
   ---------------------------
  或
   ---------------------------
   ORA-06550: 第 1 行, 第 7 列:
   PLS-00306: 调用时参数数量或类型错误
   ORA-06550: 第 1 行, 第 7 列:
   PL/SQL: Statement ignored.
   ---------------------------
  或
   ---------------------------
   Access violation at address 00000000. Read of address 00000000.
   ---------------------------
   有的时候还会出现“Access violation at address 06053AF8 in module 'dbexpora.dll'. Write of address 00000008.”这个错误提示。 这是系统调用dbexpora.dll时出现的异常。
 
    于是无限郁闷。@_@
    于是移师到Delphi7下继续调试。(潜意识中,我对Delphi5、Delphi6和Delphi7的感情,是不一样,虽然它们一脉相承。奇怪吧?我也不知道这来源于哪里,也不能说倾向于那一个,但就是在感情上是感觉不一样的,乖乖~~~不知道各位是不是有这种感觉 ⊙_⊙ )

    世事难预料。
    想不到在D7下捣鼓了一会儿就搞定了。
    想不到比用ADO调用还更简单方便!  ⊙ō⊙

    由于以前没有用过dbExpress做过项目,对dbExpress是不怎么了解的。只是大概的知道D7中新增加了TsimpleDataset,替换了D6中的TSQLClientDataSet。通过这次调试,发现D7版的dbExpress的确是新增了一些新东东,比D6版确是有很多改进,更易用且安全快捷!

    今天上午又做了一个简单的三层Demo,做了同样测试,发现也很爽! ^ō^

    现在我就把在D7下用dbExpress调用Oracle存储过程(返回数据集)的整个调试过程和在调试过程中的一些想法写下来,给有这方面需求的朋友做一下参考,同时也作为我在大富翁论坛上的第一篇写作笔记。 ^_^


二、几点说明:
 
   在开始之前,先做几个短暂的总结性说明,这样介绍下面的操作步骤时就会更容易说明白:

   1.D6和D7的dbExpress版本是不一样的,Delphi6中是dbExpress 1.0,Delphi7中是dbExpress 2.0。dbExpress作为Borland新一代的跨平台的数据访问引擎,是大有发展潜力的,跨平台是其最大特点。但是dbExpress自身目前仍需完善(比如现在支持的数据库还不是很多、健壮性有待提高等)。
让我们拭目以待,期待Delphi8的问世。^_^


   2.用Delphi6调试Oracle的存储过程的时候,发现有如下情况:
   
   <1>将存储过程写在Oracle的包内,当TSQLStoredProc连接到TSQLConnection时,对象监视器StoredProcName属性中,下拉列表是空白,显示不出任何存储过程的名字,         TSQLStoredProc没有自动得到当前的任何存储过程。(可以手工添加存储过程名,形式如“包名.存储过程名”)
   
   <2>将存储过程写在Oracle的包外,当TSQLStoredProc连接到TSQLConnection时,           TSQLStoredProc能自动得到当前表空间中的存储过程名称,在对象监视器StoredProcName属性的下拉列表中可供选择。所以在上面提到的CSDN的帖子中RockYuan说“存储过程放包外面”,指的就是这么回事。

   3.D6和D7的dbExpress中TSQLStoredProc组件的属性是有一些不同的。最明显的是 D6的TSQLStoredProc只有StoredProcName这个属性,而D7的的TSQLStoredProc除了有StoredProcName这个属性外,还有个属性PackageName。这就使我们能够方便的调用Oracle中的包及包中的存储过程。
     这一点很重要(太伟大了!),我们下面要用到PackageName这个属性的。


   4.既然D7的TSQLStoredProc组件提供了PackageName属性,我们就把存储过程按照惯例写在包内,PackageName属性对应包名,StoredProcName属性对应存储过程名,这样易于组织、管理和方便使用,逻辑上也清晰。(这就是“懒”的好处。有名人曾说过:这个社会之所以在进步,是因为人们总是想变的更“懒”一点。还有一句话叫做“有资源不用等于浪费!”^ō^ )


三、调试过程:

   下面就开始一步一步来调试,先说明一下我的调试环境:
     D6的调试操作环境: Win2K Pro + D6 + upd2 + Oracle817  
     D7的调试操作环境: Win2K Server + D7 + Oracle817
   
   好的,现在开始写这个示例了:

   1。在Oracle数据库中建立数据表,结构如下:

     /* TSRES是表空间的名称,Subject是表名称 */  
      CREATE TABLE "TSRES"."Subject"(
          "ID"    NUMBER(16)   NOT NULL,
          "CODE"  VARCHAR2(20),
          "TITLE" VARCHAR2(40),
          "DXDM" VARCHAR2(20),
          constraint PK_Subject primary key ("ID")
         );


    2。在SQL_PLUS中创建程序包,如下:

     /* 程序包 */  
      CREATE OR REPLACE  PACKAGE "TSRES"."QUERY_PACKAGE"  AS
   
        /* 定义游标 */  
        TYPE My_Cursor IS REF CURSOR;  

        /* 查询过程 */
        procedure Subject_Select(DXDM_X in TSRES.Subject.DXDM%type,
                            Result_Data out Query_PACKAGE.My_Cursor);
   
       End QUERY_PACKAGE;


    简单说明:因为是要返回查询结果的,所以是要使用到游标的。在这里声明了一个弱性游标类型My_Cursor,我们调用存储过程进行数据查询的工作就全靠它的游标变量来完成了。
游标的具体的使用说明,请参阅PL/SQL相关的书籍。


   3。程序包体创建如下:

    /* 程序包体 */  
     CREATE OR REPLACE  PACKAGE BODY "TSRES"."QUERY_PACKAGE"  AS
   
      /* 做简单的查询,带有一个传入参数 */
       procedure Subject_Select(DXDM_X in TSRES.Subject.DXDM%type,
                            Result_Data out Query_PACKAGE.My_Cursor)
        is
          begin
            open Result_Data for
             select  TSRES.Subject.Code,TSRES.Subject.Title
             from    TSRES.Subject
             where   TSRES.Subject.DXDM=DXDM_X   /* DXDM_X是需要传递进入存储过程的参数 */
             order by TSRES.Subject.ID;
          end Subject_Select;                                        
        end QUERY_PACKAGE;

    简单说明:在程序包体中写了一个查询过程,声明DXDM_X是传递进入到存储过程的参数,用in来标识,声明Result_Data是Query_PACKAGE包中的My_Cursor游标类型的游标变量,是out型参数。open Result_Data语句就是打开游标,用来返回SQL查询的数据结果。
    注意:包和包体的编写一定要正确,不然我们无法进行下面的工作。这个可以在Oracle中做简单的测试。同时,这个也是整个操作过程中的难点。

   4。到此Oracle数据库表结构和需要测试调用的存储过程创建完毕。

   5。向数据表TSRES.Subject中添加若干条记录以备查询之用。
 
     如:  ID      Code     Title      DXDM  
           1       01       河北     100000
           2       02       山东     100000
           3       01       河南     100000
           4       04       黑龙江   100001
           5       05       吉林     100001
           6       03       辽宁     100001
           7       06       陕西     100002
           8       03       甘肃     100002

 
    6。打开D7,New -> Application新建工程Project1,选择dbExpress页框,放置TSQLConnection、TSQLStoredProc 于 Form1 上为SQLConnection1和SQLStoredProc1。


    7。SQLConnection1组件的属性设置:

     <1> SQLConnection1.ConnectionName:='OracleConnection';
       在下拉列表中选择OracleConnection,此时SQLConnection1的其他属性如DriverName、LibraryName、VendorLib等会自动设置完成。
       
    注意:在Delphi6中此处操作选择是SQLConnection1.ConnectionName:='Oracle';因为D6和D7所带的dbExpress版本不同,所以看到的ConnectionName名字不同。      
   具体的请打开/Program Files/Common Files/Borland Shared/DBExpress目录下的dbxconnections.ini文件,看看便知。

   DriverName所列出的是dbExpress目前支持的数据库驱动的名字。请打开/Program Files/Common Files/Borland Shared/DBExpress目录下的dbxdrivers.ini文件,会看到这个ini文件的第一小节 [Installed Drivers] 中所写的dbExpress支持的数据库驱动名称。
    dbExpress 1.0支持4种数据库的驱动,dbExpress 2.0支持6种数据库的驱动。

    LibraryName是dbExpress提供的相应数据库的dll文件的文件名,因为我们选择的是OracleConnection (Oracle数据库),所以此处会显示dbexpora.dll,假如我们选择的是DB2Connection(DB2数据库),则会显示dbexpdb2.dll,这些dll文件是在/Program Files/Borland/Delphi7/Bin目录下的。上面提到的在D6下调试的时候出现的“Access violation at address 06053AF8 in module 'dbexpora.dll'. Write of address 00000008.”错误,便是LibraryName属性指定的dbexpora.dll文件在程序内部的交互中出现了严重问题,这也是dbExpress 1.0不够健壮的表现。        
   
    D6的dbexpora.dll 大小为161 KB (165,376 字节),D7的dbexpora.dll 大小为167 KB (171,008 字节) ,嗯,有所加强。:) 这些动态链接库文件很重要,下面我们还会再谈到它们。

    VendorLib根据字面意思理解应该是数据库厂商提供的dll文件的文件名。选择OracleConnection (Oracle数据库)后,VendorLib属性值为oci.dll。这个dll文件可以在本机的/oracle/ora81/bin目录下找到,是oracle817自带的动态链接库文件,大小为87.0 KB (89,088 字节)。
    需要说明的是:不管您只选择安装了Oracle8i的客户端还是安装了数据库,oci.dll这个dll文件是都会在您的机器中存在的。


    <2>Params设置如下:

       DriverName=Oracle     //驱动
       DataBase=Develop      //这是数据库的名字
       User_Name=jrq         //用户名  
       Password=jrq          //密码
       RowsetSize=20         //Params其他项保持默认即可
       BlobSize=-1
       ErrorResourceFile=
       LocaleCode=0000
       Oracle TransIsolation=ReadCommited
       OS Authentication=False
       Multiple Transaction=False
       Trim Char=False

    注意:因为D6和D7的dbExpress版本是不一样的,所以会发现在Delphi6中SQLConnection1.Params的参数数量和顺序是与Delphi7中不一样的。请区别对待。

   <3>设置SQLConnection1.LoginPrompt:=False;
      TSQLConnection这个属性的作用,是和BDE中TDatabase及ADO中TADOConnection的LoginPrompt属性的作用是一样的。^ō^
      这个就不用多说了。

    当上面<1><2><3>步设置完成之后,点击SQLConnection1.Active属性。
    如果能成功的选择True,则说明已经成功的连接到Oracle数据库了。
    看到了吧,dbExpress连数据库的确是挺简单的! ^ō^

    上面<1><2>操作的另外一种设置方法是:双击Form1上的SQLConnection1图标,这时就会弹出设置窗口来,选择Oracle驱动,设置数据库的属性等等,在此不一一细说。

    <1><2><3>步设置完毕后,如果此时打开上面提到过的dbxconnections.ini文件看看,就会发现刚才所做的所有配置都在里面保存着。就这么简单!:)


    8。保持SQLConnection1.Active状态为True,进行SQLStoredProc1的设置,如下:

       SQLStoredProc1.SQLConnection:=SQLConnection1; //连接 SQLConnection1
 
       SQLStoredProc1.PackageName:='QUERY_PACKAGE';//选择包名。D7版的这个属性很重要。

       SQLStoredProc1.StoredProcName:='SUBJECT_SELECT'; //选择存储过程名。
   
    PackageName 和 StoredProcName,本示例中最终要的两个属性终于露面了。PackageName属性对应Oracle数据库中程序包名,StoredProcName属性对应程序包体中的存储过程名,看看dbExpress为我们提供的服务是多么周到细致啊!
    在本示例中,Delphi与Oracel整合成败的关键一步就在于此(世事难预料啊)!
    如果SQLStoredProc1 的 PackageName 和 StoredProcName属性都能按照上面所写的显示并能做正确的选择,那么值得恭喜:已经成功了一多半!⊙ō⊙

    这是多么令人激动的、使人兴奋的、值得骄傲的事情啊!(这是小学写作文时的常用语 ⊙ō⊙ )


    9。将SQLConnection1.Active状态设为False(这样节约系统资源 ^ō^),接着进行下一步操作。


    10。放置TButton于Form1上为Button1。


    11。写Button1的OnClick事件如下:
   
    procedure TForm1.Button1Click(Sender: TObject);
    begin
       //----连接数据库----
       if not SQLConnection1.Connected then
          try
             SQLConnection1.Connected:=True;
          except
             //处理异常
             Application.MessageBox('数据库连接失败!','错误信息',MB_OK+MB_ICONERROR);
             Exit;
          end;

       //---执行存储过程----
       with SQLStoredProc1 do
          begin
             SQLConnection:=SQLConnection1;
             Close;
             Params.Clear; //清除参数

             PackageName:='QUERY_PACKAGE';             //程序包
             StoredProcName := 'SUBJECT_SELECT' ;                   //程序包中的过程
             Params.CreateParam(ftString ,'DXDM_X',ptinput) ;       //生成ptinput型参数,注意此处的DXDM_X对应存储过程的in型参数。
             Params.CreateParam(ftcursor, 'Result_Data',ptoutput) ; //生成ptoutput型参数,注意此处的Result_Data对应存储过程的out型参数。
             Params.ParamByName('DXDM_X').Value:='100001';      //参数赋值,将此值传递给存储过程,用来做SQL查询的条件。
             try
                Open;  //--执行存储过程--
             except
               //--处理异常--
                Application.MessageBox('执行存储过程失败!','错误信息',MB_OK+MB_ICONERROR);
                Exit;
              end;
          end;

         //----处理返回数据结果集,此处只是做简单的显示工作。----
         //----您可以在此处进行更负责的处理,比如中间层向客户端返回数据等工作。----
         with SQLStoredProc1 do
            begin
               First;
               while Not Eof do  //--数据集循环,显示得到数据--
                   begin
                       ShowMessage(FieldByName('Code').AsString+'--'+FieldByName('Title').AsString);
                       Next;
                   end;
             end;
    end;

   简单说明:
    CreateParam函数原型为:function CreateParam(FldType: TFieldType; const ParamName: string; ParamType: TParamType):TParam;
    写参数时要注意FldType、ParamType的类型需要和调用的存储过程的参数类型一致。这一点非常重要,也是难点,写语句的时候请仔细对照参数属性和类型,使其对应一致。
 
   访问返回的数据集数据,用SQLStoredProc1.FieldByName('字段名').AsXXXXX 就可以了。
   其中的'字段名'是存储过程中SQL语句的查询字段的名称。


   12。OK,F9,如果编译不出错,就会运行。(这是大实话)


   13。真正激动人心的时刻到了,请屏住呼吸!
       运行程序,点击Form1上的Button1按钮,会弹出ShowMessage的窗口。
       此时显示的就是在Oracle存储过程查询到的数据。爽吧?  ^ō^  
       OK!大功告成!
       可以长出一口气了,可以cheer庆祝了! ^O^


 四、画蛇添足:
 
    针对这个示例,意犹未尽的再补充几点说明和一些想法:

    1.把上面说的东东搞定了,转换到在三层结构中客户端向中间层的查询就容易多了:中间层的远程数据模块(RDM)上放置dbExpress组件,搭建中间层环境。用Type Library注册查询方法GetDate(...),然后在客户端调用XXXXX.AppServer.GetDate(...)就OK了。
     三层的具体操作过程略,请参阅与多层分布式相关的文章书籍。

     另外,上述示例还没有来得及在Kylix下做测试。因为Kylix3中也是dbExpress 2.0版本,因此原则是可以运行的。您可以在New -> CLX Application 新建一个CLX工程做测试。CLX 的意思是Component Library for (cross)X-platform――跨平台组件库,这样的工程可以把程序移植到Kylix下编译运行。

    2.由于dbExpress引擎是不使用数据缓存的,数据访问是单向的。处理数据虽快了,但也是有代价的,不能按照传统的常规数据集来操作,给应用操作也造成了很多不便之处(我的观点)。我第一次接触dbExpress是在Kylix2下,然后才转到Delphi下的。那时候还不知道dbExpress的特性,想在TDBGrid中显示数据,谁知一用,当时就傻了!⊙_⊙

     由于一些数据感知组件如TDBGrid等是需要用到数据缓存的,这和dbExpress组件的存取机制是矛盾的。所以当打开数据集时会出现如下内容的警告框:“Operation not allowed on a unidirectional dataset”!
     这一度让我以为是Kylix的TBDGrid有问题。:(  当时还气冲冲的在大富翁发了一个帖子质问TBDGrid,链接如下:
     http://www.delphibbs.com/delphibbs/dispq.asp?lid=1216409

     后来才知道针对dbExpress操作中用TDBGrid显示数据,或修改提交数据的问题可以有两种可操作的方法,如下:
   
      <1>用dbExpress -> TDataSetProvider -> TClientDataSet -> TDataSource ->TDBGrid 这套组件的组合来操作。

       这里用到了TdataSetProvider,Delphi多层分布式编程中最重要的组件。它具有数据缓存机制,所以这就能够使dbExpress操作数据的能力变得从容简单并且具有张力,显示数据和处理数据的方式变得方便并且逻辑清晰。

       熟悉多层分布式的朋友看到这套组件的组合时,肯定会立刻感到惊奇:这个微型架构差不多就是通常的三层结构中常用的架构模式!呵呵,可以称作是“伪三层”的。
我当时的感觉是:太神奇了!。⊙_⊙

       Delphi再一次向我们展示它的玄妙之处、微妙所在!!

      <2>使用D6的TSQLClientDataSet或D7的TSimpleDataset。

     这两种方法在上面提到的那个帖子中都提到了并讨论过,可以作为参考查阅。

    3.终于该说TsimpleDataSet组件了,上面没有详细的谈到它。TsimpleDataSet是一个有神奇特点的组件,它是D7特有的组件。打开它的SimpleDataSet.DataSet属性集看看看就会知道。  

     关于SimpleDataSet.DataSet.CommandType属性,D7的帮助文件中是这么写的:
     
       CommandType       Corresponding CommandText
      ------------       ----------------------------------------------
       ctQuery            An SQL statement that the dataset executes.

       ctStoredProc       The name of a stored procedure.

       ctTable            The name of a table on the database server. The SQL dataset
                          automatically generates a SELECT statement to fetch all the
                          records on all the fields in this table.

     也就是说设置TsimpleDataSet的DataSet.CommandType属性,它就可以对应到传统意义上TQuery、TStoredProc和TTable组件,可以作为DataSet数据集组件来使用。它的运行参数或SQL语句是写在DataSet.CommandText中的。

     另外,TSimpleDataSet自身包含了一个Connection的属性集,设置这个属性集就能连上数据库独立运行,也就是说TSimpleDataSet可以单独使用而不需要TSQLConnection的支持,它自成一家,并且它是支持数据缓存的(这一点就决定了它可以和数据感知组件搭配在一起为人民服务了)。

     所以TsimpleDataSet组件在dbExpress中的地位和作用是不言而喻的。
     所以D7中用TSimpleDataSet代替D6中的TSQLClientDataSet,是不无道理的。:)

     不过我要说的是:在这次调用Oracle存储过程(返回数据集)的调试过程中,我顺便用了一次TsimpleDataSet。把它的DataSet.CommandType设置为ctStoredProc,然后调用Oracle的存储过程,发现还是存在问题的。出现异常,提示的什么错误信息,忘了。:)

     并且还发现它只能自动得到写在Oracle程序包外的过程,写在包内的却得到不到(就是说DataSet.CommandText的下拉列表中是空白),关于这一点,和前面说过的在D6中用TSQLStoredProc组件调试时发生的情况非常相似。

    4.由于这一段时间在搞JAVA,李维先生的那本《Delphi7高效数据库程序设计》半年前买回来就束之高阁了。刚才特意拿出来,翻了翻关于TSQLStoredProc组件介绍的部分。在第73页下方看到一段话,现在摘录下来(用扫描仪扫下来的,几乎没有错字 ^_^):

    “对于会返回数值的存储过程而言,当使用TSQLStoredProc组件调用时,在存储过程执行完毕之后,存储过程会把返回的数值存储在TSQLStoredProc组件的Params特性值中。程序员只需要调用Params的ParamByName方法,并且以存储过程返回的数值的名称作为ParamByName方法的参数即可。但是Delphi7联机帮助在这个地方说错了。要想调用会返回数值的存储过程,程序员仍然需要调用TSQLStoredProc组件的ExecProc方法,而不是设置Active特性值为True或是调用 Open方法。”

      看完后很是诧异,因为上面我写的代码中就是用的TSQLStoredProc组件的Open方法,但是并没有发现出了什么错误。

      于是赶紧看了看D7中关于SQLStoredProc.ExecProc方法的联机帮助,如下:

      ----------------------------
       Call ExecProc to execute the stored procedure specified by StoredProcName
       if it does not return a cursor. ExecProc returns the number of rows affected
       by the stored procedure. This becomes the value of the RowsAffected property.
       To speed performance, an application should ordinarily prepare the stored
       procedure by setting the Prepared property to true before calling ExecProc
       the first time.
       Note:Do not use ExecProc for stored procedures that return a cursor. When the
       stored procedure returns data, use the Open method or set the Active property
       to true.
      ----------------------------

     E文水平差,勉强看懂。就我个人观点来看:帮助文件中说的是没有错的。尤其是最后Note中说的两句,已是点睛之笔了!

     我又将上述代码改了一下,做了一次测试,改用ExecProc方法执行存储过程,却发现当执行到SQLStoredProc1.First的时候,Delphi7提示“SQLStoredProc1: Cannot perform this operation on a closed dataset”的异常警告。

     至此,我得出结论:TSQLStoredProc的ExecProc方法,和传统意义上的数据集的ExecProc方法在使用上其实质并无出入和差别,它们的使用方法应该是一样的(该用Opne的时候就Open,该用ExecProc的就ExecProc),TSQLStoredProc和传统意义上的数据集在此处扮演相同的角色。
   
     又看了看李维先生写的范例,是使用的InterBase数据库。按道理来讲TSQLStoredPro的ExecProc方法操作应该和数据库无关的。我的计算机上现在没有安装InterBase,不知道是否真的和这个有关系。
 
     在这里我无心论证李维先生说的是否有偏颇,也不知道他是如何得到这个结论的,只是对这个说法提出置疑。当然如果有哪位大虾知道这其中的奥妙,麻烦请您请通知我一下,我在等着您的解惑。
     我提出这个问题我也先暂时保留,以备日后参考,或留做再进一步测试之用。⊙_⊙


    5.关于程序发布的问题。开始的时候,单独copy了用D7编译出来上述示例的exe程序,到另外一台装了Oracle的计算机(已经安装了同一个Oracle数据库的客户端)上运行,发现是有问题的,无法执行存储过程,很是奇怪。后来想到了dbExpress引擎连接数据库需要上面说过的一些dll文件,有可能是没有发布dll文件造成的。
      于是到/Program Files/Borland/Delphi7/Bin目录下找到 dbexpora.dll这个文件(就是上面提到的大小为167 KB (171,008 字节)的那个),把它copy到需要发布程序的计算机的/WINNT/system32目录下(不需要用REGSVR32.EXE进行注册),然后再运行exe程序。嘿嘿,一切OK,运行良好,畅通无阻!^0^

     于是得到另外一个结论:用dbExpress编译的数据库程序发布时,需要连同相关的dll文件一块发布。用到那种数据库,就需要发布dbExpress提供的与此数据库相关的动态链接库文件――把相关的dll文件copy到系统system32目录下或放到和exe程序同一目录下即可。
     在Delphi7中,dbExpress提供的支持的6种数据库驱动引擎的dll文件分别是:
        [DB2]        dbexpdb2.dll
        [Interbase]  dbexpint.dll
        [MySQL]      dbexpmysql.dll
        [Oracle]     dbexpora.dll
        [Informix]   dbexpinf.dll
        [MSSQL]      dbexpmss.dll

     其实这些数据库对应的dll文件在上面提到的dbxdrivers.ini文件中写的很清楚,很明白,一看便知。

     假如是开发的三层分布式结构的程序,在发布时,除了在中间层服务器上发布AppServer中间层服务程序和MIDAS需要的东东外,再把dbExpress提供的相关的dll文件也一起发布了就可以了。这个没有做测试,理论上应该是酱子的(这个样子的^_^),应该不会有太大的困难的。 :)


    6.jcc老兄在D6下把dbExpress调用oracle返回数据集存储过程的问题搞定了,我的D6还是不行,还需要和jcc多多交流,看看问题出在了哪里。
      如果有那位大虾也在D6下搞定了,麻烦请您请通知我一下,我正在翘首等着您的帮助呢。谢谢您。

    7.用ADO调用Oracle存储过程(返回数据集)也是很头疼的(这一类相似问题的存在也就代表了有这一类相似的需求,这也算是我写这篇东东的一个初衷),TADOStoredProc组件似乎存在某些缺陷,当时用ADO快把我搞疯掉了,不过总算是搞定了。
     请参看我以前为搞这个问题的帖子(仅供参考而已),同时也感谢热心帮助我的朋友:
       http://www.delphibbs.com/delphibbs/dispq.asp?lid=1321421

    8.一直没有机会用dbExpress做实际应用的有价值的东东,都是东一榔头西一棒槌的学点感兴趣的,所了解的仅仅只是停留在表层上并且很粗糙,对一些深入的东东一无所知。深感水平有限,以上说的不对的地方请您多多指正。表示感谢。
     联系方法 Email:[email protected]    QQ:1142610

    9.发现越罗嗦越多了,打字打的手也累了,赶紧打住,喝杯茶。:)

      想了想,有时间的话,看看李维先生的《Delphi7高效数据库程序设计》还是很有必要的。我现在就开始准备看了。@_@


                                                                                                                                                         by  jrq
                                                                                                                                                   2003.10.31 下午 于石市



   PS:刚刚看到一个笑话,也贴上来给大家舒缓一下神经。

        《杨先生出差前的夫妻对话》

      杨:亲爱的,明天一早我要出差了。
      杨嫂:噢,到哪里出差?
      杨:一个遥远的地方。
      杨嫂:很远吗?有多远?
      杨:近点的二百公里,远点的有三百公里吧!
      杨嫂:多给你带点衣服吧?
      杨:不用,单位特意给我做了新衣服,全套的。
      杨嫂:你出国呀,是哪个国家?
      杨:哪个都不是。
      杨嫂:多带点钱吧!
      杨:用不着花钱!
      杨嫂:和同事开车去?
      杨:不是,就我一个人,坐船去!
      杨嫂(疑惑):你到底去哪里出差?
      杨:秘密,现在还不能说!
      杨嫂:告诉我哪边总行吧,东西南北哪个方向?
      杨:都不是!
      杨嫂:事儿多吗?会不会很累、很辛苦?
      杨:不会,就在船上躺着,有时侯往下看看,其他的也没什么事儿!
      杨嫂:会不会很寂寞?
      杨:不会,很多人看着我呢!
      杨嫂:自己路上小心点!
      杨:这由不得我!
      杨嫂:你什么时候回来?
      杨:后天一早。
      杨嫂:到哪里去接你呀?
      杨:草原!
      杨嫂(忍无可忍):杨利伟,你到底要去哪里?去干什么?
      杨:地球人都知道!  



       ^ō^ 

 

     
 

链接: http://www.delphibbs.com/delphibbs/dispq.asp?lid=2268230

 
 2003-11-4 13:24:14    补记


刚才又在Kylix3中把上述示例调试了一下,运行无误,调试过程和D7中几乎就是一模一样的。

下面做一些补充:

1.调试环境:
   RedHat7.1 + Kylix3 + Oracle817 for Linux

   BTW:我的K3是装在/user/local/目录下的。

   另外,因为在RedHat8/9下,我无论如何都无法成功安装Oracle (中文安装模式更糟),所以一直都在用RedHat7.1,郁闷!【@_@】


2.Delphi7和Kylix3中dbExpress的一些对比:

   和Delphi7中对应的dbxconnections.ini和dbxdrivers.ini两个文件,在Kylix3分别是usr/local/etc/dbxconnections.conf 和 usr/local/etc/dbxdrivers.conf。

   dbxconnections.conf文件大小为1.25 KB (1,290 字节),dbxdrivers.conf 文件大小为2.15 KB (2,208 字节)。


   打开dbxdrivers.conf配置文件。摘录一部分内容,如下所示:
   --------------------
   [Installed Drivers]
   Interbase=1
   MySQL=1
   PostgreSQL=1
   DB2=1
   Oracle=1
   Informix=1

   [Interbase]
   GetDriverFunc=getSQLDriverINTERBASE
   LibraryName=libsqlib.so
   VendorLib=libgds.so
   Database=database.gdb  
   RoleName=RoleName
   User_Name=sysdba  
   Password=masterkey
   ServerCharSet=
   SQLDialect=1
   BlobSize=-1
   CommitRetain=False  
   WaitOnLocks=True
   ErrorResourceFile=./DbxIbErr.msg
   LocaleCode=0000
   Interbase TransIsolation=ReadCommited

   [MySQL]
   GetDriverFunc=getSQLDriverMYSQL
   LibraryName=libsqlmy.so
   VendorLib=libmysqlclient.so
   HostName=ServerName
   Database=DBNAME
   User_Name=user
   Password=password
   BlobSize=-1
   ErrorResourceFile=./DbxMySqlErr.msg
   LocaleCode=0000

   [PostgreSQL]
   GetDriverFunc=getSQLDriverPGSQL
   LibraryName=libsqlpg.so
   VendorLib=libpq.so
   HostName=ServerName
   Database=test
   User_Name=user
   Password=password
   BlobSize=-1
   ErrorResourceFile=./DbxPGSQLErr.msg
   LocaleCode=0000

   [DB2]
   GetDriverFunc=getSQLDriverDB2
   LibraryName=libsqldb2.so
   VendorLib=libdb2.so
   Database=DBNAME
   User_Name=user
   Password=password
   BlobSize=-1
   ErrorResourceFile=./DbxDb2Err.msg
   LocaleCode=0000
   DB2 TransIsolation=ReadCommited

   [Oracle]
   GetDriverFunc=getSQLDriverORACLE  
   LibraryName=libsqlora.so
   VendorLib=libclntsh.so
   DataBase=Database Name
   User_Name=user
   Password=password
   BlobSize=-1
   ErrorResourceFile=./DbxOraErr.msg
   LocaleCode=0000
   Oracle TransIsolation=ReadCommited
   RowsetSize=20
   OS Authentication=False
   Multiple Transaction=False
   Trim Char=False

   [Informix]
   GetDriverFunc=getSQLDriverINFORMIX
   LibraryName=libsqlinf.so
   VendorLib=libinfclient.so.1.0.0
   HostName=ServerName
   Database=Database Name
   User_Name=user  
   Password=password
   BlobSize=-1
   ErrorResourceFile=./DbxInfSqlErr.msg
   LocaleCode=0000
   Informix TransIsolation=ReadCommited
   --------------------

   在[Installed Drivers]部分描述了Kylix3中dbExpress支持的数据库引擎。
   
   对比一下dbxdrivers.ini和dbxdrivers.conf,就会发现如下结果:
   D7中dbExpress支持的6种数据库是:
        DB2
        Interbase
        MySQL
        Oracle
        Informix
        MSSQL

   而K3中dbExpress支持的6种数据库是:
        Interbase
        MySQL
        PostgreSQL
        DB2
        Oracle
        Informix

    另外,因为本示例中使用的是Oracle数据库,对应Delphi7中SQLConnection1的LibraryName和VendorLib属性所指定的动态链接库文件,在Kylix3中是:
     LibraryName=libsqlora.so
     VendorLib=libclntsh.so

    其中,libsqlora.so文件在user/local/kylix3/bin/目录结构下,对应的libsqlora.so.1.0文件大小为306.5KB(313,880字节)。
    libclntsh.so文件在oracle的安装路径/oracle/product8.1.7/lib/目录结构下,对应的libclntsh.so.8.0文件大小为6.4M(6,712,733 字节)。


3.Kylix3中的调试代码:

   在KDE图形界面中,Start Menu --> Borland Kylix3,点击Kylix3(Delphi IDE),即可启动Kylix3的Delphi IDE界面。或在命令行模式中使用 # startdelphi 命令也可。

   在IDE中New Application,新建工程Project1。
   剩下的工作数据库的链接、配置工作是和D7中一样的,不一一叙述。
 
   针对本示例,K3中的程序部分代码如下所示,和D7中的代码几乎是一样的:

   ---------------------------------

   implementation

   {$R *.xfm}

   procedure TForm1.Button1Click(Sender: TObject);
   begin
      if not SQLConnection1.Connected then
         try
            SQLConnection1.Connected:=True;
         except
            //--Exception --
              Application.MessageBox('DataBase Server Eroor','Error',[smbOK],smsWarning);
              Exit;
         end;

      with SQLStoredProc1 do
          begin
              SQLConnection:=SQLConnection1;
              Close;
              Params.Clear;

              PackageName:='QUERY_PACKAGE';                         //package name
              StoredProcName := 'SUBJECT_SELECT' ;                   //Storedprocedure name
              Params.CreateParam(ftString ,'DXDM_X',ptinput);       //Create parameter:ptinput
              Params.CreateParam(ftcursor,'Result_Data',ptoutput);  //Create  parameter:ptoutput
              Params.ParamByName('DXDM_X').Value:='1000000001';     //value
              try
                  //ExecProc;   //---- 注意此处仍是不能用的 ExecProc 方法的。^_^
 
                  Open;         //--Open DataSet--
              except
                   //--Exception --
 Application.MessageBox('StoredProc Error','Error',[smbOK],smsWarning);
                   Exit;
              end;
           end;

       //----ShowMessage----
       with SQLStoredProc1 do
            begin
               First;
               while Not Eof do  //--While do--
                   begin
                       ShowMessage(FieldByName('Code').AsString+'--'+FieldByName('Title').AsString);
                       Next;
                   end;
            end;
    end;

   ----------------------------------

   提醒:如果有兴趣,可在D7中建立 CLX Application,把上述代码Copy进来,不用做任何改动就能无误运行!^_^


                                                   
                                                                  by  jrq

                                                               2003.11.4 上午

 

 2003-11-8 8:50:49    有一个问题

看到一个帖子说的是dbExpress和Oracle7.3不能协同工作,会提示找不到oci32.dll。

而D6和D7的dbExpress在dbxdrivers.ini配置文件中中指定了 VendorLib=OCI.DLL。

因为从没有用过Oracle7.3,不知道是不是这个oci32.dll是Oracle7.3特有的(对应Oracle8i中的OCI.DLL)?
 
如果哪位装有Oracle7.3,有兴趣的话可以帮忙看一下。

你可能感兴趣的:(oracle存储过程)