PB基础知识版资料_第1页
PB基础知识版资料_第2页
PB基础知识版资料_第3页
PB基础知识版资料_第4页
PB基础知识版资料_第5页
已阅读5页,还剩120页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

PB基础知识

.数据库的事务管理

在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另•种状态。为

确保数据库中数据的•致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数

据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从

起始点以后的操作应全部回退到开始状态。

对事务的操作是这样进行的:先定义开始•个事务,然后对数据作修改操作,这时如果提

交(COMMIT),这些修改就永久地保存下来,如果回退(ROLLBACK),数据库管理系统将放弃您所

作的所有修改而回到开始事务时的状态。此外有些数据库支持事务的"存储点(savepoint)这

一概念:即在一个事务进程中任意一点您都可以进行当前状态的存储,回退时只是回到你所设

定的存储点,而不必退回全部的事务。如果您的事务可以分成儿组对数据库的修改,那就可以

设置多个存储点,根据需要您可以回退到任意一个存储点,而不使所有事务的修改数据全部丢

失。

正确地管理事务可以保证数据的完整性,当您所做的工作全部完成和得到确认之前,没有

任何数据物理地写进数据库。让我们来看这样一个实例,我们有这样一个银行应用系统,前台

使用者作出将储户甲的•百元存款划归储户乙帐下的操作;在后台的数据库中,这两个客户的

记录分储在两张表中,当使用者在屏幕上作出如上操作时,在后台需要对两张表进行修改。如

果在数据库中对甲用户存款余款作减去一百元修改后,对乙用户加一百元的操作修改却失败

时,前张表也必须回到修改前的状态,否则数据库的内容不统一,甲储户白白损失一百元,信

息必然是不正确的。因此进行事务管理是必须的。

传统地,我们认为•个事务包括了对一个或多个表的修改,而随着分布式数据库和数据仓

库的发展,事务可能包括了对一个或多个数据库的修改。在上例中甲乙两用户就可能是异地

用户,信息分储在不同地域的不同数据库中,上述的•个事务就涉及到了对不同数据库的操

作。

PowerBuilder中的事务管理作为数据库的前台开发工具PowerBuilder支持事务管理的

操作。在PowerBuilder中有一种称作事务(transaction)的对象,这个对象是PowerBuilder应

用与数据库的通讯区域。PowerBuilder在应用开始时建立一个全局的事务对象SQLCA。山

于大多数的应用只用到一个数据库,所以•般开发者主要也只用SQLCA作为与唯一数据库连

接的事务对象。

PowerScript中常用的事务管理的语句有四

个:COMMITROLLBACK,CONNECT,DISCONNECT。

当您需要应用与数据库建立连接时使用CONNECT这•操作命令,取消连接时执行

DISCONNECT;这两个命令一般分别用在应用的开始和结束,也就是Application的Open和

Close事件中。

当一个事务的数据库修改都成功地完成后,修改须提交给数据库,COMMIT语句是一个

旧事务结束和一个新事务开始的界线。在修改被提交前,数据库的数据并没有被真正地修改,

这些修改被保留在某个工作区,只有作修改的用户才能看到这些被修改后的值,提交之后,则

所有的用户就都可以看到新值了。

在事务的进程中发生某些错误,或者在操作中出于种种原因打算中止事务,须用

ROLLBACK命令回退事务,如果已作的操作不用ROLLBACK命令取消,这些操作必将错误地作

为下一个事务的一部分而导致数据库的混乱。

如果您使用的是多窗口的应用,却只用一个事务对象,就应格外注意ROLLBACK和

COMMIT会影响事务的逻辑,致性。在某个窗口执行的这两个指令会使其他窗口应用中所

进行到一半的工作提交或回退。

在多用户系统中,修改和提交的时间越接近,提交成功的可能性就越高。因为一个事务中

所有的SQL语句全部执行成功而提交却失败是完全可能发生的,例如在您的事务过程中,另--

个用户修改了数据并提交,这很可能使您作出的修改无效,这时COMMIT将失败,您必须回退

这一事务的全部。

事务对象的AutoCommit属性事务对象有一个AutoCommit的属性可以使开发者简化

对事务管理的操作,这一布尔型的属性可以用TRUE或FALSE来对其赋值。当其为真

时,PowerBuilder不通过其他额外的交互就将您的SQL语句传输给后台数据库,而且执行完毕

自动提交。

当然,您可以设置AutoCommit属性为假(缺省值),使用COMMIT或ROLLBACK这样的关

键词提交或回退事务。在大多数应用中,一部分的数据库操作是要成组提交的,而另一些则不

用。因此我们可以利用AutoCommit的特性来确定事务的起点,当我们把AutoCommit的属

性设为False时,系统设定此时为事务的起点。当AutoCommit设为真时,系统自动消取这一

事务。因此你可以先把AutoCommit设为真,当您需要开始一个事务时,将其置为false,此刻

即为事务起始点。

PowerBuilder内部这种事务管理的最大优点是方便。您不去考虑整个事务,而只需把您

所作的修改提交或滚回即可。但是方便与可控性总是矛盾的,在PowerBuilder中没有存储点

和嵌套事务管理的机制,即使您所使用的数据库支持这些特性,在PowerBuilder中却无法得以

体现。不过在普通的应用中,存储点和嵌套事务管理并不是必须的,一般的事务管理足以够

用.

用数据库的事务管理指令实现完全控制上述的事务管理方式尽管简单方便,但是在某些

应用中,我们也的确需要利用所用的数据库系统的嵌套事务和存储点的特性,而PowerBuilder

内部的事务管理没有提供这样的功能,您必须自己设计。

自己进行事务管理的方式是直接使用数据库本身的事务指令。当您使用自己的管理方

式时,就应使PowerBuilder停止管理事务,即设置AutoCommit为TRUE,系统内部就不会自动

建构事务处理的命令了。实现人工事务管理的方式是采用EXECUTEIMMEDIATE这条

PowerBuilder指令来执行任意的数据库操作。你所需做的是将数据库指令编辑成一个字符

串,您可以执行任何的数据定义语句如建表、建主键、存储过程等,例如您可以用

EXECUTEIMMEDIATEBEGINTRANSACTIONtransname

这样的指令开始一个事务。采用这种方法,只要您所用的数据库支持嵌套事务和存储点

等事务管理,我们通过PowerBuilder开发出的应用也就同样可以实现。

在PowerBuilder中提供的事务管理的方法是多种多样的,只要您灵活运用,就一定能设计

出优秀的数据库应用来。

3.PowerBuilder面向对象的程序设计

在PowerBuilder的应用开发中,运用面向对象技术不是必须的,但是对于一个大型复杂应用系

统的开发,如果采用了面向对象技术,则开发效率会大大提高。用PowerBuilder开发的好

处是,即使您不懂得什么是面向对象技术,您也可能会下意识地用到面向对象技术的一些特性,

但是如果您对这一技术有了充分的了解,那对您所设计程序的可重用性,可维护性和其他各方

面的质量都会有大幅度提高。

PowerBuilder的对象

PowerBuilder对象有三个部分元素:属性、函数和事件。(1)属性:也就是数据。包括

系统属性和用户定义属性两类,描述该对象的各种特性。如在窗口对象中的系统属性包括标

题、高度、宽度等,用户定义的属性可以是实例变量或共享变量,对这种属性的访问同系统属

性相同,只是在封装性上有所不同。⑵事件:PowerBuilder中的事件与Windows的事件

存在着映射关系。当用户的操作或系统本身产生了Windows的标准事件,就转化成

PowerBuilder中的事件。例如命令按钮有-一个叫Clicked事件,当用户按按钮时触发,操作系

统首先检测到鼠标被点击,并把Clicked这一消息转给该按钮,PowerBuilder运行时的工作引

擎翻译了这一事件,并执行适当的PowerScript语句。PowerBuilder中大部分的事件都是可以

映射成山用户激发的Windows事件。另•类事件是用户定义事件,这种事件是用户声明并

可以在任何时刻由用户触发的,PowerBuilder中保留了75个用户事件可由程序员使用。

⑶函数:用户通过函数来使对象完成某些操作。PowerSoft公司建议用户通过调用对象事件

和函数来修改对象而不要直接修改对象属性,以满足对象封装的要求,例如采用Window.hidef)

而不要用Window.visible=false来使窗口不可见。

PowerBuilder面向对象的特性

提到一门语言的面向对象特性,它应当具有继承性、多态性和封装性,如缺少其中的一个

则只能称其为基于对象的系统,而不是面向对象的系统,而PowerBuilder则很好地具备了全部

三个特性。•继承:PowerBuilder中,窗口、菜单和用户对象是可以继承的,而其他对

象则都不能。当您继承了•个对象,那您得到的子类将具有父类的属性、实例变量、共享变

量、控件、用户自定义事件、对象级函数、事件和代码(script)。也就是说当您继承了一个

类,您几乎得到了这个类的全部,不过有一点值得注意,您不能在子类中删除任何一个继承到

的特性。在继承了祖先类后,您可以在子类中扩展或覆盖祖先的元素。例如代码,在

PowerBuilder中缺省的是采用扩展方式,先执行继承到的祖先代码,后执行子类的扩展代码。

您也可以选择菜单Compiler|OverrideAncestorScript选项,只执行子类的代码,并可在代码中

的任意位置调动祖先代码如重载函数是面向对象程序设计的重要特

CALLw_l::oPen«

性,即同一函数名却有着不同的参数和返回值。在运行时,系统自动寻找执行参数相匹配的那

个函数,例如我们常用的MessageBox。这个函数就有12个不同参数的重载函数。遗憾的是,

对PowerBuilder我们不能在同一个继承对象中定义2个不同参数的重载函数,只能在后继的

类中定义重载函数,这使我们对重载函数的定义带来了很大的不便,这不能不说是

PowerBuilder的缺陷。(不过在即将发布的PowerBuilder5.0中,正是支持了函数重载这•特

性。)对于对象的继承,PowerBuilder中所有对象都有其共同的基类PowerObject,从这一

个基类下面分成继承类GraphicObject和NonVisualObject等。它们的继承关系和扩展函数

及属性如下表:•多态性:在PowerBuilder中有大量的多态函数如print。、

TriggerEvent()等,在运行过程中,您只需要指出对象和函数名即可。在有些函数中,即使不知道

对象类,也可以用ClassName。函数得到对象类,或得到实例名,将对象名作为函数参数调用该

函数。•封装性:封装的目的是为了实现数据隐藏和数据保护,封装的目标是为对象

提供•个对外操作的接口,使其他对象通过函数来访问,而不允许直接操纵对象的属性。在

PowerBuilder中有三种访问类型Public、Protect、Private,这三种访问控制类型可以用在对象

的变量和函数上,缺省的实例变量和对象函数都是public类型的。为了保护数据,应尽可能多

地使用private和protect类型,前者只允许对象内部的元素来访问,后者可以接受对象内部和

继承类的元素访问。

PowerBuilder面向对象程序设计的过程

事实上用户在运用PowerBuilder进行程序设计的一开始,就已经开始进行对象类的设计:

例如在建立窗口时可以用窗口画笔建立一个新窗口,命名为建立的窗口就

(painter)w_empo

是从PowerBuilder的窗口对象继承下来的,所以窗口会自动具备一些属性,例如X和Y的坐

标、高度、宽度、窗口类型、标题等。当用窗口画笔建立窗口时,您给这些属性赋了值,你还

可以增加窗口的函数和事件。当窗口被保存时,你就建立了一个新的窗口继承类,称

为:w_emp。输出对象的句法显示的部分程序如下:globaltypew_empfromWindow

intX=200

intY=233

intWidth=2405

intHeight=1285

booleanTitleBar=true

stringTitle="EmployeeMaintenance"

stringMenuName=,,m_emp"

longBackColor=12632256

booleanControlMenu=true

booleanMinBox=true

booleanMaxBox=true

booleanResizable=true

endtype

globalw_empw_emp请注意系统在这里做了什么:首先定义了w_emp窗口类是从

window类继承而来,接着定义这一对象类的各属性值,在句法的最后一行声明了一个全局变

量,变量的类型(对象类)是w_emp,变量名(对象实例)也是w_empo也就是说在您建立了

w_emp这一窗口对象后,系统就具有了w_emp这一对象类和w_emp这一全局变量。这个

变量是引用变量,也就是在有些语言称之为指针的变量类型。引用变量并不真正含有窗口实

例的属性,而是包含了一个位置或是内存中的一个地址,在那儿你可以找到实例的实际属性和

程序。但在应用的开始时这•指针为空,因为在内存中并没有一个这样的窗口实例。当系统

执行了Open(w_emp)后,系统在内存中复制了一份w_emp对象类的拷贝或实例,w_emp这一

参照变量指向了这一地址。由于窗口对象的visible等属性都是Public类型,所以继承类也都

具有了这些属性。

2005011414:37:00

5.DataWindow的打印输出

在PowerBuilder应用程序的开发中,各种报表的打印功能是必不可少的。PowerBuilder4.0提

供了大量与打印机进行通信的内部函数,用于各种打印输出。运用这些函数,您不仅可以打印

常见的DataWindow对象,还可以打印字符串、位图、图形对象,甚至是全屏。

DataWindow的两种打印方法:在PowerBuilder中我们可以有两种方法打印DataW

indows,.一种最简单的方法就是直接地将DataWindow提交给打印机,另一种方法是将大量

的DataWindow(或混合其他的对象)放在一起作为一个单一的打印作业提交输出。法一:

不指定打印作业,而发送一个单•的DataWindow。将报表传给打印机输出的最简单的方式

是使用DataWindow对象的Print。函数,您只需简单指明您打印的DataWindow控件的名字

就可以得到该DataWindow生成的报表了。例:dw_data.print({canceldialog})Print()

函数不需要任何参数,但是它有一个可选参数,可以控制PowerBuilder是否显示PrintCancel

对话框窗口(缺省为自动出现,如图1所示)。Canceldiabg参数是一个布尔型变量,其缺省值为

真。一Print:HPLaserJetHPonLPTl:Preparingpagelof2Cancel图1:除非特别

指定,否则将在打印时缺省出现PrintCancel对话框。Print。函数还有一些格式可控制打印正

文字符串,但是它们要求首先打开个打印作业。这些格式将在后面解释。法二:集成多

个DataWindow作为一个打印作业。您可以将提交打印的多个DataWindow作为一个单一

的打印指令。为实现这一功能必须打开一个所谓打印作业(Printjob),即发送给打印机一个单

-工作单元,其中可以包含多个子任务。在PowerBuilder中」■个打印作'也由它的作业代号唯

一指定。我们使用PrintOpen({jobname})函数打开一个作业,并得到打印作业号。PrintOpen()

函数有一个可选参数,就是打印作业名称,这个作业名字将作为Windows3.x的PrintManager

作业集中的名字。当您使用PrintOpen()函数时,系统将另起一页开始打印。当然在打印作业

中,您还可以使用其他函数换页,比如将在后面讲述的PrintPage。函数。在您打开了一个

作业号后,就可以在这个作业内使用下列函数继续其他各种各样的打印项目。1)在打印

作业中加入字符串:我们也可以使用另外一个与Print。函数略微不同的函数来打印字符

串:PrintText(printJob_number,string,x,y{,font_number})这个函数将包含在string参

数中的正文字符串当作•个对象进行打印。这个打印的字符串在由X,Y参数(以千分之寸

为单位)指定的坐标处开始。font_number参数是••个可选参数,缺省使用当前字型,否则

font_number的值可以从0到8,0是打印机缺省的字型,18是使用后面讲述的

PrintDefineFont()函数定义的字型。这个函数不像其他指定坐标的函数,它将改变打印光标的

位置。这个函数的返回值是打印光标的新X坐标,但它不改变Y坐标。

在打印作业中打印对象

下面的这些函数可以使您在打开的打印作业中打印不同的对象。这些函数中的任一个

指定X、Y坐标的函数都不会改变当前打印光标位置。

objectname.Print(printjob_number,x,y{,width,height})这个函数可以将任何•个

object_name指定的对象放在print_job_number定义的打印作业中打印,这个对象可以是一

个窗口也可以是一个DragObject类的继承类(包括了所有的窗口控件)。X、Y参数指定了一

页中的坐标,您可以指出所打印对象出现的位置(以千分之一寸为单位)。Width和Height参

数是可选的参数,用来指明你所需的打印对象的宽和高(仍以千分之一寸为单位)。如果缺

省,PowerBuilder将使用对象本身的尺、『打印。

RrintBitmap(printJob_number,bitmap,x,y,width,height)这个函数将在山X,Y参数(千分之

一寸为单位)指定的打印区域上打印一个位图。bitmap参数是一个包含要打印位图的文件名

的字符串(例:BRUSH.BMP)。Width和Height参数指定位图显示的宽和高,这个参数并不是可

选的。输入0值,意味着位图按它本身的值打印。

PrintDataWindow(printJob_number,datawindowcontrol)这个函数将一个DataWindow控

件的内容作为这个指定的打印作业的•部分来打印。由于PowerBuilder使用在

DataWindow对象中定义的字型,PrintDefineFont()和PrintSetFont。函数不对其产生影响。

Powersoft建议如用PrintDataWindow,那么在这个打印作业中就不需要使用其它函数。因为

这个函数将使用整张纸,每一次调用PrintDataWidnow,您打印的下一项都将另起一页。

这个函数将打印一指定宽度的线。这条

PrintLine(PrintJob_number,XI,Y1,X2,Y2,thickness)

线将在由XI,Y1参数(以千分之一寸为单位)指定的坐标处开始,在由X2,Y2参数(也以千分之

一寸为单位)指定的坐标处结束。thickness参数是一个整型变量,它指定了这条线的宽度,以

千分之一一寸为单位。PrintOval(printJob_number,x,y,width,height,thickness)这个函数

打印一个椭圆(如果宽度和高度相同则形成一个圆)它的轮廓线的宽度由thickness指定。椭

圆都由一个想象的边框包围,X,Y参数指定了这个界框的左上角的位置(千分之一寸为单

位),width,heigh为这个边框的高和宽。

除了它是打印一个矩形外,这个函

PrintRect(printJob_number,x,y,width,height,thickness)

数与PrintOval。函数完全一样。

PrintRoundRect(print_job_number,x,y,width,height,thickness)除了它是打印一个圆角矩形

外,这个函数与PrintOval()函数完全一样。

2005011414:37:00

利用SetActionCode函数控制DataWindow

DataWindow控件的一些事件有一个动作码操纵这个事件之后的缺省动作。在Pow

erBuilder4.0中我们可以使用SetActionCode函数,来设置这个动作码的值以控制在这些事件

发生后的处理过程。(在PowerBuilder。中,由于事件可以有返回数值,所以采用返回一-个整

型数值来取代SetActionCode函数,比如使用returnl取代SetActionCode⑴,但基本的使用规

则两者是相同的。)DataWindow控件中下列事件使用动作码:CLICKEDDBERROR

ITEMCHANGEDITEMERRORPRINTPAGERETRIEVEROWRETRIEVESTART

UPDATESTART有效的动作码值和应当的处理过程随事件的不同而不同。

ClickedEvent

无论何时,当用户在DataWindow控件上点击时,CLICKED事件被触发。如果点击在--个

有效的行上,那么DataWindow将自动把此行作为当前行。如果你不想换行,就可以使用

SetActionCode来停止。。进行换行和CLICKED事件。(缺省)1停止处理CLICKED事

件。例如:下面一段代码只允许用户点击在自己的用户号上。〃ClickedEvent

//We'llassumethereisaninstancevariablewiththecurrentuser's//UserlD:stringis_user_id

longll_rowstringls_user_idH_row=GetClickedRow()

//Noneedtocontinueiftheuserdidn'tclickonavalidrowifll_row<lthenreturnendif

ls_userJd=GetltemString(ll_row/,user_id")

//IftheuserjdisnotthecurrentuserthendisallowrowchangeiflsuseridOisuseridthen

beep(l)SetActionCode(l)returnendif

ItemChangedEvent

DataWindowlTEMCHANGED事件可以有几种不同的操作:接受前一字段的新值,因有错误

而拒绝接受新值,拒绝新值但是继续其它的处理过程。这些值如下:。接受新的数据值。

(缺省)1拒绝新的数据值。(启动ItemError事件)2拒绝新的数据值但是焦点改变。

在ITEMCHANGED事件中使用SetActionCode函数可以进行多字段的交叉确认。例如,银行

系统中为确认Accountstatus是否可以转为Inactive,就需检验Balance字段是否为零:

//ItemChangedEventDecimal(2)ld_balance//CustomerAccountBalance

Longll_currow//CurrentRowNumber

Stringls__column_name//Thenameofthecolumnthatchanged

Stringls_status//CustomerAccountStatusll_currow=this.GetRow()

ls_column_name=this.GetColumnName()CHOOSECASEIs_column_name

CASE"STATUSnls_status=this.GetText()//IfSTATUSisInactiveIFIs_status='T'THEN

ld_balance=this.GetltemDecimal(ll_currow,"BALANCE")IFId_balanceOOTHEN

//SETANERRORthis.SetActionCode(l)RETURNELSE//ACCERTTHEVALUE

this.SetActionCode(0)/*notrequiredsinceOisdefault*/RETURNENDIFENDIF

ENDCHOOSE在程序中,SetActionCode函数不一定要在最后一行,但是由于其他

DataWindow函数可能会重置动作码。为了避免这个问题,一般在SetActionCode后面立即执

行Return结束这个程序段。在ITEMCHANGED事件中使用SetActionCode函数用途是

可以给该字段一个新值,而不是用户输入的那样。例如:用户将日期输入为星期日,但是我们

希望将其改为在此之后的第一个非休息日。实现这一功能并不像想象的那样简单:

//ItemChangedEventdateldt_process//processdatelongll_currow//CurrentRowNumber

stringls_column_name//ThenameofthecolumnthatchangedH_currow=this.GetRow()

ls_column_name=this.GetColumnName()CHOOSECASEIs__column_name

CASE"process_date"ldt_process=f_get_next_bus_date(date(this.GetText()))错误

this.SetText(ldt_process)this.AcceptText()...执行上述代码,系统将进入死循环。

因为用AcceptText函数改变日期的同时,也触发ITEMCHANGED事件,只是当前列仍在process

data列上,这样就导致堆栈溢出。因此在ITEMCHANGED事件中不能使用AcceptText函数,应

使用我们这里介绍的SetActionCode这--函数来完成这一功能:CASE"process_date"

ldt_process=f_get_next_bus_date(date(this.GetText()))正确

this.SetltemText(ll_currow,"process_date",ldt_process)//setvalueinbuffer

this.SetActionCode(2)//rejecteditcontrolvalueRETURN在PrimarylBuffer中将

process_date的值置为ldt_process,SetActionCode(2)摒弃用户在edit控件中输入的值(星期

日),并允许改变焦点(没有错误发生)。

ItemErrorEvent

在任何时候,当一个DateWindow列没有通过有效性检验或者这个值在ITEMCHANGED

事件中被拒绝时,ITEMERROR事件启动。如同ITEMCHANGED事件一样,它的动作码也可以设

置为接受或拒绝这个字段的新输入值。它还可以在拒绝新值时,决定是否取消错误信息框的

显示。ITEMERROR事件的动作码可以是:0拒绝新的数据值并且显示错误信息乂缺省)

1拒绝新的数据值而不显示错误信息;2接受新的数据值;3拒绝新的数据值但是允

许改变焦点。如果我们想要在一特定区域显示一个用户自定义的错误信息来代替

PowerBuilder本身错误信息框,我们可以使用SetActionCode来取消标准的信息框。例如,在

前面例子中,当收支差额不是0是0时,我们就可以用这一方法显示一个错误信息:

//ItemErrorEventLongll_Currow/*CurrentRowNumber*/

Stringls_column_name/*Thenameofthecolumnthatchanged*/ll_currow=this.GetRow()

ls_column_name=this.GetColumnNameCHOOSECASEs_column_nameCASE"status"

MessageBox("Error",''Accountcannotbechangedtolnactive"+"Balanceisnotzero.")

this.SetActionCode(l)RETURN...ENDCHOOSE在ITEMERROR事件中使用

SetActionCode,我们就可以有选择地忽略DataWindow对象的一个列中输入的有效性规则。

例如,在收支差额中,我们有下面这个有效性规则:Real(GetText())<=10000客户收支

差额不应超过10,000元,如果我们允许使用公司帐户的客户可以超过10,000,我们可以使用

如下方式:"ItemErrorEventCHOOSECASEIs_column_nameCASE"balance"

//Allowbalanceover$10,000onCorportateaccountsIFId_balancenot<

=10000ANDIs_type="C"this.SetActionCode(2)RETURNENDIF...我们也可

以像前面ITEMCHANGED事件那样在PrinaryiBuffer中拒绝新输入的值并填入新值,只是在这

里将动作码置为3。

DBErrorEvent

在执行了dw.Retrieve,dw.Update()函数或嵌入式SQL语句并发生了一个数据库错误

(SQLCode等于1)时,触发DBError事件。许多PowerBuilder的开发商都在为这种情况设计

了标准的数据库错误信息显示。为了使PowerBuilder不显示缺省的数据库错误信息,我们可

以使用SetActioncode。DBError事件的动作码值如下:。显示错误信息。(缺省)1不

显示错误信息。例如:〃DBErroreventMessageBox,数据库错误“,"错误值

"+string(this.DBErrorCode)+&"错误信息为/+this.DBEirorMessage(),StopSign!)

//SupressPBgeneratedDBErrorMessage.this.SetActionCode(l)return

PrintPage

PRINTPAGE事件是在执行dw.Print。函数之后,数据传送给打印机之前触发。当打印一个

DataWindow时,你可在打印之前设置动作码来跳过一页。PRINTPAGE事件的动作码如下:

0不跳过一页;(缺省)1跳过一页。如您打算打印时跳过一页,你可以在中PRINTPAGE

编码如下:〃Printpageeventthis.SetActionCode⑴

RetrieveRow

从数据库服务器中每次接受了一行记录均启动REIRIEVEROW事件。在这个事件中,你可

以设置一动作码来停止检索。下面是RETRIEVEROW事件的有效动作码:0继续。(缺省)

1停止检索。如果一个DataWindow将命中很多行,并且你希望在检索到一定量后停

止。你可以在RETRIEVEROW事件中使用SetActionCode://RetrieveRowevent

//lnstancevariableLongil_count...IFil_count++>100THEN

//Maximumrowsretrieved^tipretrievalthis.SetActionCode(l)RETURNENDIF当

用来给被检索行计数的临时变量il_count的值超过100时,检索将停止。注意:在

RETRIEVEROW事件中存在代码,那么检索每一行都会触发事件,这将降低检索的速度。

RetrievestartRETRIEVESTART事件在dw.Retrieve()函数之后,产生SQL传送给服务器之前

触发。在•些特定场合可能需要在开始♦个检索之前停止它。在Retrievestart事件中

的动作码:0继续。(缺省)1不检索。例如:我们让一个用户输入检索标准的窗

口,在RETRIEVESTART中判断返回行数是否太多,以决定停止检索并且让用户缩小检索范围。

//RetrieveStarteventlntli_count/*ExpectedRetrieveCount*/...

//GetcountofRowstoberetrieved...IFIi_count>1000THEN

,,,,

MessageBox(Stop","Pleasenarrowyoursearchzstop!)This.SetActionCode⑴RETURN

ENDIF...

UpdateStart

这一在执行Update。函数之后,产生的修改SQL语句传送给服务器之前触发。通过

设置这一动作码,你可以阻止修改传送给服务器。UPDATESTART事件的动作码如下:0继

续。(缺省)1不修改。如果你要阻止执行修改语句,在UPDATESTART事件中使用下

列代码:〃UpdateStartevent...this.SetActionCode(l)RETURN...

综述

在很多情况下,设置动作码是非常有用的。这里的例子,让你对其中几种情况有一个了

解。当你对使用PowerScript编码有了更多的经验后,你会发现SetActionCode是非常有用

的。

2005011414:37:00

DataWindow的数据缓冲区

DATAWINDOW的四个缓站区

在PowerBuilder中,DataWindow是用户前端用来存储、操纵数据的对象。在每一个

DataWindow对象中有4个二维表作为数据缓冲区,用来存储查询到的数据。用户在

DataWindow中对数据处理系统内部的操作实际上都是将数据在这几个缓冲区中进行的修

改和移动,最后在用户提交数据库时,系统根据这四个缓冲区中的信息形成SQL的IN

SERT,UPDATE,DELETE等语句。这四个缓冲区是:

PrimaryBuffer

这个缓冲区是存放填充窗口中DataWindow控件中数据的,调用DataWindow的

Retrieve。函数和InsertRow。函数可以将数据填入这个缓冲区中。当使用有关DataWindow

删除和过滤函数时,相应记录将从这一缓冲区中删除。而在执行DataWindow的Update。函

数时,PowerBuilder将查看这一缓冲区中的记录,以形成SQLINSERT和UPDATE语句。

DeleteBuffer

这个缓冲区保存的是用DeleteRow()函数从PrimaryBuffer中删除的记录,执行Update。函

数时,系统根据这一缓冲区的记录形成DELETE语句。

FilterBuffer

这个缓冲区存储的是从OriginalBuffer使用Filter。函数过滤到PrimaryBuffer中后剩余的

记录。

OriginalBuffer

这一缓冲区存储的是DataWindow最初执行retrieve。函数时得到的全部记录。当提交

数据库时,根据PrimaryBuffer生成的UPDATE语句和根据DeleteBuffer生成的DELETE语句都

要依据这一缓冲区来构造这些SQL语句中的Where子句。OriginalBuffer由

PowerBuilder内部维护,PowerBuilder所提供的任何函数都无法改变它的值,不过通过

PowerBuilder所提供的Getltem…系列的函数可以读出DataWindow最初从数据库中查到的

原始值。通过这些函数我们可以编程实现所谓的"Undo"功能,并且得到在使用乐观锁时形成

提交数据库的WHERE子句。如果您当前使用的DataWindow没有设置修改的权力,您

将不能对Delete缓冲区和Original缓冲进行操作,而且当调用Update。时也将引起系统错

误。

数据缓冲区的状态值

PrimaryBuffer和DeleteBuffer都有行级和列级的状态值,这个状态值是一个枚举类型。

在提交时由该行的状态值来决定是否要产生SQL语句,其中PrimaryBuffer产生的是INSERT

和UPDATE语句,而DeleteBuffer产生的是DELETE语句。我们用GetltemStatus()函数和

SetltemStatus。函数可以对这一状态值作操纵。这一枚举状态有以下四种:・NotModified!

一该行或行的值为查询所得,没有发生改变。・DataModified!该行或列的值

为查询所得,发生了改变。・New!该行或列为•插入的新行,数据没有发生改变(数

据为空或缺省值)。,NewModified!该行或列为•插入的新行,数据发生改变。改

变是通过用户键盘输入或调用了Setltem。函数。让我们来看下面这一实例:我们有

这样一张表,表中有三个字段,其中ITEM是主键。ITEMCHAR(5);NAMECHAR(20);

QUANTITYINTo在Script中我们查询这张表的记录,得到以下这些信息存储了在Primary

和OrigianalBuffer中,其中的行号是缓冲区加上的。在窗口中,我们编程过滤掉数量为0

的行,并且加上一个空行:dw_l.SetFilter("quantity=O")dw_l.Filter))

dw_l.lnsertRow()这时PrimaryBuffer的状态为:在FilterBuffer中的记录为:用户

在新插入行中输入数据,删除了第3行数据,并修改了第2行数据。当他离开这个

DataWindow时,Primary和Delete缓冲区的状态如下:这时执行dw_l.update。函数,系统

将基于这两个缓冲区生成SQL语句。在PrimaryBuffer中,状态为NotModified和New!

的行将被忽略而不产生SQL语句。状态为DataModified的行将产生UPDATE语句,状态为

NewModified的行将产生INSERT语句,在Delete缓冲区中的行将产生DELETE语句。

四个缓冲区在编程中的运用

某些DataWindow控件的函数有指定DataWindow缓冲区的功能。如果缺省,则表示

Primary缓冲区。下列是可以指定缓冲区的函数:•GetltemStatus。,GetNextModified()

,GetUpdateStatusf),SetltemStatus()此外还有Getltem…系列的函数,用以查询

DataWindow中的值。这些函数有:・GetltemDate(),GetltemDataTime(),

GetltemDecimal()•GetltemNumber。,GetltemStringO,GetltemTime()

Getltem…系列函数也可以指定缓冲区,而且可以允许您指定查询的是当前值还是原始值(最

初从数据库中查到的值)。如:dW-l.GetltemStringfl/'name",Primary!,TRUE)使用

DataWindow的Reset。函数和Retrieve。函数以及改变DataObject属性时,系统将重置这几个

缓冲区。当用户插入一个新行时,编程者往往要自动在新插入行中的某些列中插入一些

缺省的数据值。这时,用户并没有对数据作任何修改。可是在用户要关闭这一窗口时,如果在

CloseQuery事件中用ModifiedCount()函数查看数据是否修改,得到的结果显然是有所改变。

用户将得到一个提示信息框:"是否打算保存所作的修改?是/否/取消"这显然是不适合

的:而且如果用户选择的是保存时,这些原应是空记录的行也成为了有内容的行,系统将其形

成SQL的INSERT语句提交数据库,这样的结果更为恶劣。为改变这状况,我们可以用

SetltemStatus()函数将该行的状态改为New!。即当有新行插入并且设置了缺省值之后,该行

的状态值为New!。下面的程序就可以得到如上的功能。〃lnsertanewrowindw_:l

longl_Rowl_Row=dw_l,lnsertRow(dw_l,GetRow())

dw_l,Setltem(l_Row/'discount_pct",0,10)//

SetthrowstatustoNewisothattheCloseQuerycheckwillonlydetectuserentries.

dw_l.SetltemStatus(l_Row,0,Primary!,New!)使用SetltemStatus函数时有一些状态转换的

限制,下表中显示的是将原来的状态改变为另一状态时,会产生的影响。若表中为N。,则说明

用这•函数进行这样的设置无效;若表中给出了不同的状态,则该状态是对您指定的状态的替

代。当一个状态的改变不能允许时,可以多次调用SetltemStatus以得到期望的状态。例

如,从New!状态不能改成NotModified!状态,可将其改成DataModified!状态,然后再改成

NotModified!状态。

2005011414:38:00

游标的使用

提到游标这个词,人们想到的是在屏幕上一个闪动的方框,用以指示用户将要输入字符的位

置。而在关系型数据库的SQL语言中,游标却有另外的含义,它是存放结果集的数据对象。

为什么要用到游标

在某些PowerBuilder应用程序的开发中,您可能根本用不到游标这样一个对象。因为在

其它工具开发中很多需用游标实现的工作,在PowerBuilder中却已有DataWindow来代劳

了。事实上,DataWindow不仅可以替代游标进行从后台数据库查询多条记录的复杂操作,而

且还远不止这些。但是同DataWindow和DataStore相比,游标也有其自身的优点,比如系统

资源占用少,操作灵活,可根据需要定义变量类型如全局、实例或局部类型和访问类型如私有

或公共等。

游标的操作

使用游标有四种基本的步骤:声明游标、打开游标、提取数据、关闭游标。

声明游标

象使用其它类型的变量一样,使用一个游标之前,首先应当声明它。游标的声明包括两个

部分:游标的名称;这个游标所用到的SQL语句。如要声明一个叫作CustomerCursor的游标

用以查询地址在北京的客户的姓名、帐号及其余额,您可以编写如下代码:

DECLARECustomerCursorCURSORFORSELECTacct_no,name,balanceFROMcustomer

WHEREprovince="北京";在游标的声明中有一点值得注意的是,如同其它变量的声明一样,

声明游标的这一段代码行是不执行的,您不能将debug时的断点设在这一代码行上,也不能用

IF...ENDIF语句来声明两个同名的游标,如下列的代码就是错误的。IFIs_prov="北京

"THENDECLARECustomerCursorCURSORFORSELECTacct_no,name,balance

FROMcustomerWHEREprovince=":|匕京";ELSE

DECLARECustomerCursorCURSORFORSELECTacct_no,name,balanceFROMcustomer

WHEREprovince〈〉"北京";ENDIF

打开游标

声明了游标后在作其它操作之前,必须打开它。打开游标是执行与其相关的一段SQL语

句,例如打开上例声明的一个游标,我们只需键入:OPENCustomerCursor;由于打开游

标是对数据库进行一些SQLSELECT的操作,它将耗费一段时间,主要取决于您使用的系统性能

和这条语句的复杂程度。如果执行的时间较长,可以考虑将屏幕上显示的鼠标改为

hourglasso提取数据当用OPEN语句打开了游标并在数据库中执行了查询后,您不

能立即利用在查询结果集中的数据、您必须用FETCH语句来取得数据。,条FETCH语句一

次可以将一条记录放入程序员指定的变量中。事实上,FETCH语句是游标使用的核心。在

DataWindow和DataStore中,执行了Retrieve。函数以后,查询的所有结果全部可以得到;而使

用游标,我们只能逐条记录地得到查询结果。已经声明并打开••个游标后,我们就可以将

数据放入任意的变量中。在FETCH语句中您可以指定游标的名称和目标变量的名称。如下

例:FETCHCustmerCursorINTIs_acct_no,:ls_name,:ll_balance;从语法上

讲,上面所述的就是一条合法的取数据的语句,但是•般我们使用游标却还应当包括其它的部

分。正如我们前面所谈到的,游标只能一次从后台数据库中取一条记录,而在多数情况下,我

们所想要作的是在数据库中从第一条记录开始提取,一直到结束。所以我们一般要将游标提

取数据的语句放在一个循环体内,直至将结果集中的全部数据提取后,跳出循环圈。通过检测

SQLCA.SQLCODE的值,可以得知最后一条FETCH语句是否成功。一般,当SQLCODE值为0时

表明一切正常,100表示L1经取到了结果集的末尾,而其它值均表明操作出了问题,这样我们可

以编写以下的代码:lb_continue=Truell_total=0DOWHILEIb_continue

FETCHCustomerCursorINTIs_acct_no,:ls_name,:ll_balance;

lfsqlca.sqlcode=OThenll_total+=ll_balanceElselb_continue=FalseEndlf

LOOP循环体的结构有多种,这里提到的是最常见的一种。也有的程序员喜爱将一条

FETCH语句放在循环体的前面,循环体内再放置另外一条FETCH语句,并检测SQLCA.SQLCODE

是否为100»但是这样做,维护时需同时修改两条FETCH语句,稍麻烦了些。

关闭游标

在游标操作的最后请不要忘记关闭游标,这是一个好的编程习惯,以使系统释放游标占用

的资源。关闭游标的语句很简单:CLOSECustomerCursor;

使用Where子句子

我们可以动态地定义游标中的Where子句的参数,例如在本例中我们是直接定义了查询

省份是北京的记录,但也许在应用中我们要使用一个下拉式列表框,由用户来选择要查询的省

份,我们该怎样做呢?我们在前面曾经提到过,DECLARE语句的作用只是定义一个游标,在

OPEN语句中这个游标才会真正地被执行。了解了这些,我们就可以很方便地实现这样的功

能,在DECLARE的Where子句中加入变量作参数,如下所示:

DECLARECustomerCursorCURSORFORSELCECTacct_no,name,balanceFROMcustomer

WHEREprovince=:ls_province;〃定义ls_province的值OPENCustomerCursor;

游标的类型

同其它变量一样,我们也可以定义游标的访问类型:全局、共享、实例或局部,游标变量

的命名规范建议也同其它变量一样。

游标的高级技巧

尽管目前基于SQL语句的后台数据库所支持的语言都大致相当,但对游标的支持却有着

一些差异,例如对滚动游标支持。所谓滚动游标,就是程序员可以指定游标向前后任意一个方

向滚动。如在Informix中,您甚至还可以将游标滚向结果集开头或末尾,使用的语句分别是

FETCHFIRST,FETCHLAST.FETCHPRIOR和FETCHNEXT。当程序员用FETCH语句,其缺省是指

FETCHNEXT,由于滚动是在数据库后台实现的,所以滚动游标为用户编程提供了极大的方

便。对游标支持的另一个不同是可修改游标。上述游标的使用都是指只读游标,而象

Oracle.Sybase等数据库却另外支持可作修改的游标。使用这样的数据库,您可以修改或删

除当前游标所在的行。例如修改当前游标所在行的用户的余额,我们可以如下操作:

UPDATEcustomerSETbalance=1000WHERECURRENTofcustomerCursor;删除当前行

的操作如下:DELETEFROMCustomerWHERECURRENTOFCustomerCursor;但是如果

您当前使用的数据库是Sybase,您需要修改数据库的参数,将游标可修改的值定为1,才能执行

上述操作。这一赋值在连接数据库的前后进行均可。SQLCA.DBParm="Cursorllpdate=l"

另外一个内容是动态游标,也就是说您可以运行过程中动态地形成游标的SELECT语句。这

同在PowerBuilder中动态地使用嵌入式SQL一样,需要用到DynamicStagingArea等数据类型,

这已超出了本节的范围。

2005011414:38:00

资源管理

内存漏洞

我们在上期的《预装入对象》一文中提到了关于内存漏洞(MemoryLeak)的问题。内存漏

洞是程序员和用户都很关心的问题。一个对象被装入并且分配了内存,而在对象被关闭却

没有释放分配的内存,这时一个内存漏洞就产生了。在开发工具中也会有内存漏洞,这是

我们无法控制的,但是我们必须注意我们自己的代码所造成的内存漏洞。最有可能造成内

存漏洞的是使用CREATE语句创建一个对象后没有对应的DESTROY语句消除。无论何时,

您用CREATE函数创建了一个对象,您就必须负责在该对象执行完成之后释放分配的内存。

例:我创建了我自己的一个transaction对象:

TransactionMy_Transaction

My_transaction=CREATETransaction

当我用完这个对象后,我应该清除该对象:

DESTROYMyTransaction

(注:SQLCA是由您的应用自动创建同时也自动关闭。)内存漏洞的出现经常是与开发者

使用了非可视化用户对象有关。因为这种对象只能用CREATE语句创建它的一个实例,因此

如不对其使用DESTROY语句消除,则必然导致错误。另外一个有关的是用OpenUserObject

或OpenUserObjectWithParm函数创建的动态用户对象,这些对象同样要求您在结束使用它

们时用相应的ClosellserObject函数。我们知道,PowerBuilder能够自动清除放在一个窗口

中的常规对象,但它为什么不能在应用结束时自动清除用户自己创建的对象呢?因为

PowerBuilder仅能清除控件列表中的对象,而且只有一个对象(如窗口)及其表面的那些

对象才会列在控件列表当中(包括不可见的对象)。而动态的用户对象和非可视化对象,

与PowerBuilder全局对象的实例(transactions、error、message等等)一样,是在对象的

控制列表已经创建后加到对象上的。关闭父对象时,系统并不知道要清除这些动态加入的

对象。如果您没有手工消除它们,它们将•直保持打开状态,并常驻在内存中,直到使用

工具来清除,或者关闭Windows系统。使用象Windows3.1ResourceKit中提供的内存资源

监控器这样的工具,能使您在测试过程中检查资源以确定资源按照预想的那样被释放。

内存管理

在Windows3.x平台上,开发人员编译时会遇到这样的问题:机器里有32M内存,而且只

有两个应用在运行,可是PowerBuilder■却总是提示内存不足(OutofMemory)。而用户在

使用某应用软件时,也会同样出现内存不足的问题,于是用户只得关闭其它的应用,直到

发现关闭了某一程序释放了足够的内存空间可供PowerBuilder运行起来为止。于是用户开

始抱怨开发者,而开发者开始抱怨PowerBuilder。其实在多数情况下,他们都不应该受责

备,问题的根源出在W

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论