ORACLE应用程序开发人员指南-1

来源:互联网 发布:网络报 关键 编辑:程序博客网 时间:2024/06/05 10:28

ORACLE应用程序开发人员指南

ORACLE APPLICATIONS DEVELOPER’S GUIDE

--译者:罗勇,对原文有删减、补充、纠正,不提供翻译质量保证。

 

--序言:阅读本文档,需要有点基础,也就是会做简单的form,懂点PL/SQL,懂做form时用到的一些东西,比如画布(含堆叠画布和内容画布),数据块,参数,窗口,包等,对form的常用触发器有点理解。

1.在ORACLE应用程序中使用PL/SQL

1.1服务器端与客户端PL/SQL过程

1.1.1服务器端PL/SQL过程

服务器端PL/SQL过程(Server-Side PL/SQL procedure)指保存在ORACLE数据库中的过程。

1.1.2客户端PL/SQL过程

客户端PL/SQL过程(Client-Side PL/SQL procedure)指运行在ORACLE数据库客户机(本文一般指FORM所在的服务器,而不是用户使用的个人PC)中的过程。

1.2 PL/SQL编码普遍标准

1.2.1 尽量使用包组织PL/SQL

FORM的每个块,包括数据块和逻辑块都应该为之创建包(package).

1.2.2关于包的大小

客户端PL/SQL包单元(包单元指包说明或者包主体或者独立的过程)的源文件大小不能超过10K,其源文件和编译后的文件大小之和不能超过64K(超过64K编译会出错)。若会超过此大小,请拆分包单元,如果是包过大,可将包的私有变量和过程放到一个或者多个私有包(private package,目前我还不了解这个私有包,据称私有包内的变量和过程只有源包可以访问)中,若是单独的过程过大,可将过程拆为多个。

服务器端PL/SQL包和过程大小虽然没有限制,但是FORM在调用时生成的本地副本(local stub)却有大小限制。所以请限制包的过程数量在25个以内以防包的大小超过10K。

1.2.3为包添加新过程时添加到包的最后

不管是客户端还是服务器端PL/SQL过程,都应该如此,否则的话,需要编译每个引用该包的FORM,否则会带来ORA-4062错误。

1.2.4客户端PL/SQL包引用字段时加上数据块名做限定

一直使用数据块名加上点加上字段名来引用字段,而不是只写字段名,也就是应该使用BLOCK.FIELD_NAME而不是FIELD_NAME,因为不指定块名,oracle会所有表单中所有的块,降低效率。

1.2.5使用块名字段名调用含输入参数过程,不使用字段作为IN OUT或者OUT 参数调用

使用块名.字段名调用过程,然后使用COPY函数更新该调用字段,而不是用过程的IN OUT 或者OUT参数类型来调用过程。使用这种方法可以阻止字段在不管是否过程真的更改了该字段值都被标记为已更改的状态(being marked as changed)。过程中的OUT类型的参数在过程成功执行后都会被覆盖。举例说明:声明了一个过程test (my_var VARCHAR2 IN),应该这样调用test(‘block.field’),而不应该这样做:声明过程test (my_var VARCHAR2 IN OUT),然后这样调用它test(:block.field)。

还有个好习惯就是一个过程有多个参数时用=>符号来关联参数和传输的值。

1.2.6使用DEFAULT代替:=来声明过程参数的默认值

这是因为DEFAULT在声明默认值方面比:=更优,但是在FORM中声明常量时应该使用:=赋值,因为你是在赋值而不是在声明默认值。

1.2.7多使用OBJECT ID而不是OBJECT NAME

在对一个对象(OBJECT)使用SET_<OBJECT>_PROPERTY或者ORACLE应用程序对象库的类似方法更改多个属性时尽量使用OBJECT ID,而不是每次都使用OBJECT NAME,当然了这需要先使用类似FIND_ITEM之类的FIND_<OBJECT>方法找到其ID并赋给对应的变量类型(完整的FIND_<OBJECT>方法及返回类型见后)。当然还可以考虑将ID存放于一个包的全局变量中,这样只在FORM运行时寻找一次即可。

罗勇增加附:完整的FIND_<OBJECT>方法及返回类型(可参考FORM BUILDER帮助)

被查找对象类型

查找函数

查找函数返回值类型

Alert

FIND_ALERT

ALERT

Block

FIND_BLOCK

BLOCK

Canvas

FIND_CANVAS

CANVAS

Record Group Column

FIND_COLUMN

GROUPCOLUMN

Editor

FIND_EDITOR

EDITOR

Form

FIND_FORM

FORMMODULE

Record Group

FIND_GROUP

RECORDGROUP

Item

FIND_ITEM

ITEM

List of Values

FIND_LOV

LOV

Menu Item

FIND_MENU_ITEM

MENUITEM

Parameter List

GET_PARAMETER_LIST

PARAMLIST

Relation

FIND_RELATION

RELATION

Report

FIND_REPORT_OBJECT

REPORT

Tab Page

FIND_TAB_PAGE

TAB_PAGE

Timer

FIND_TIMER

TIMER

Tree Node

FIND_TREE_NODE

NODE

View

FIND_VIEW

VIEWPORT

Window

FIND_WINDOW

WINDOW

1.2.8 小心处理空值NULL

在用等号比较两个值时,若有任何一个值为空,则整个表达式返回false,比如a为null,b为null,那么a=b将会返回false,而不是true。所以比较稳妥的可能会涉及到空的比较的方法是((a=b) or ((a is null) and (b is null)))。附带提下,ORACLE这个文档中少了个括号,我在这里补充了一下。

1.2.9选择合适的全局变量种类

FORM BUILDER 和PL/SQL支持三种全局变量,分别是:

ORACLE应用程序开发人员指南 - Robbin - 罗勇的博客        ORACLE 表单全局变量:就是以 :GLOBAL. 跟上名称的全局变量。罗勇补充该中变量可在表单中直接使用而无需事先声明。

ORACLE应用程序开发人员指南 - Robbin - 罗勇的博客        PL/SQL包全局变量:在包说明中定义的变量。

ORACLE应用程序开发人员指南 - Robbin - 罗勇的博客        ORACLE 表单参数:在FORM设计阶段通过Object Navigator的Parameters节点创建的。

三种全局变量比较

ORACLE

表单全局变量

PL/SQL包全局变量

ORACLE 表单参数

可否在设计时创建

 

可否在运行时创建

 

 

对所有的表单可见

 

 

对附加库可见

 

支持具体数据类型

总是CHAR(255)类型

有声明式默认值

 

 

能否非直接式引用

 

可否在命令行上指定

 

 

是否需要清除以恢复其占用的内存

 

 

ORACLE表单任何代码中都可使用

 

1.3客户端PL/SQL过程 还是 服务器端PL/SQL过程?

u      调用表单内置过程(ORACLE FORMS BUILT-INS)特别是客户端的内置过程时使用客户端PL/SQL过程

u      直接引用字段,比如使用:block.field或者NAME_IN或者COPY方法,使用客户端PL/SQL过程.当然更好的办法或许是将字段值或者字段名(block.name)作为参数传递给过程,这样有助于提升代码的模块化.

u      如果一个过程包含3个或者以上的SQL语句,一般宜使用服务器端PL/SQL过程.

u      不使用SQL和不访问数据库的过程哪儿方便就放哪儿.

u      一个过程只从服务器端调用,使用服务器端PL/SQL,如果客户端和服务器端都调用,除了该过程特别复杂应放在服务器端外,其余在服务器端和客户端都应保存。

1.4美化你的PL/SQL代码

u      对于包,先定义私有变量,然后是私有过程,最后是公有过程。

u      对于包和过程结束处的end,end后最好跟上包(过程)名称。

u      使用两个字符的代码缩进方法。

u      SQL缩进方法如下所示(也是用两个字符式代码缩进,注意断行):

DECLARE

      CURSOR employee IS

            SELECT empno

            FROM    emp

            WHERE  deptno = 10

                            AND ename  IN (‘WARSHINGTON’,’MONROE’)

                            AND mgr =2701;

u      使用 -- 开始注释

u      注释行从注视符号 -- 开始缩进,而不是从注释行的语句开始。

u      如果语句肯定不需要了,删除它。

u      混合使用大小写,PL/SQL中的保留字使用大写,其余使用小写。

u      不要深层嵌套使用IF…THEN…ELSE,而应该使用IF…THEN…ELSIF,良好的格式如下所示:(此地不得不遗憾的提一句:FORMS BUILDER 6i不支持CASE语法)

IF … THEN …

ELSIF … THEN …

ELSIF … THEN …

ELSIF … THEN …

ELSIF … THEN …

END IF;

而不是如下格式

IF … THEN …ELSE

IF … THEN … ELSE

     IF … THEN … ELSE

     END IF

END IF

END IF;

u      除了过程中要处理某个异常,否则过程中不应该出现嵌套的PL/SQL块(即BEGIN/END块)。

1.5 异常处理

1.5.1 FORM中的PL/SLQ错误处理

如果出现错误想停止继续处理,先用FND_MESSAGE显示错误信息,再用RAISE_FORM_TRIGGER_FAILURE停止继续处理,语法格式如下(斜体部分根据实际情况撰写):

IF (error_condition) THEN

     fnd_message.set_name(appl_short_name,message_name);

     fnd_message.error;

RAISE FORM_TRIGGER_FAILURE;

END IF;

1.5.2 数据库过程中的错误处理

指保存在数据库的过程,先用FND_MESSAGE.SETNAME定制显示的消息,然后使用APP_EXCEPTION.RAISE_EXCEPTION来停止继续处理,FORM中不需做任何事情来处理这个异常,因为ON-ERROR触发器会处理的。语法格式如下(斜体部分根据实际情况撰写):

IF (error_condition) THEN

     fnd_message.set_name(appl_short_name,message_name);

     fnd_message.error;

APP_EXCEPTION.RAISE_EXCEPTION;

END IF;

1.5.3检测FORM_SUCCESS,FORM_FAILURE,FORM_FATAL

记住检测这些值时,这些值可能被你的内置过程触发的触发器中的另外的内置过程改变,举个例子:

GO_ITEM(‘emp.empno’);

IF FORM_FAILURE THEN

   RAISE FORM_TRIGGER_FAILURE;

END IF;

可能会有这种情况,你的内置过程GO_ITEM触发了别的出发器,比较典型的就是WHEN-NEW-ITEM-INSTANCE。虽然GO_ITEM这个内置过程可能会失败,但是WHEN-NEW-ITEM-INSTANCE这个触发器可能会成功执行,这时候FORM_FAILURE就为false了,而不是true了,解决这个问题的方法:

GO_ITEM(‘emp.empno’);

IF :SYSTEM.CURSOR_ITEM != ‘EMP.EMPNO’ THEN

   RAISE FORM_TRIGGER_FAILURE;

END IF;

1.5.4 避免使用RAISE_APPLICATION_ERROR因为它同服务器端处理异常的模式(schema)有冲突。