Lab 1: 创建客户化应⽤
本节介绍如何创建⼀个客户化应⽤。Step 1: 建⽴Directory以applprod⽤户登录操作系统运⾏:$ cd $APPL_TOP
在⽬录$APPL_TOP下建⽴⽬录satyam,其结构如图(注意⼤⼩写的区分)
图1.1
Step 2: 修改Environment File以applprod⽤户登录操作系统
修改环境⽂件 PROD.env ,增加如下2⾏:
SATYAM_TOP = “/demoapp/demoappl /satyam/1.0.0”Export SATYAM_TOP
重启ORACLE APPLICATIONS,以使更改⽣效。Step 3: 创建Tablespace/User以applprod⽤户登录操作系统运⾏:$ sqlplus
在SQL-PLUS中,以⽤户名SYSTEM连结数据库运⾏以下命令建⽴TABLESPACE:create tablespace satyam_data
datafile ' /demo/demodata/satyam_data01.dbf'size 500M reuse autoextend on next 10M ;create tablespace satyam_idx
datafile '/demo/demodata/satyam_idx01.dbf'size 100M reuse autoextend on next 10M ;运⾏以下命令建⽴USER:create user satyamidentified by satyam
default tablespace satyam_datatemporary tablespace temp ;运⾏以下命令授权:
grant connect, resource to satyam ;grant select any table to satyam ;grant select any sequence to satyam ;
如果是⼀次性导⼊的话下⾯是运⾏创建Table, Sequence, Trigger, Synonym, View,Package等的脚本,在下⼀节再讨论。Step 4: Application Setup
操作者必须先登录OA系统,并以“系统管理员”职责进⼊“浏览器”窗⼝。(1) Application Register
在功能菜单中选择“应⽤-注册”,打开“应⽤”窗⼝新建⼀条记录(具体内容如下),保存后,关闭该窗⼝
Application SATYAM CustomizationShort Name SATYAMBasepath SATYAM_TOP
Description SATYAM Customization(2) Oracle Users
在功能菜单中选择“安全性-ORACLE-注册”,打开“ORACLE⽤户”窗⼝新建⼀条记录(具体内容如下),保存后,关闭该窗⼝Database User Name SATYAM
Password 相应⼝令(系统要求输⼊2次,以确认)Privilege EnabledInstall Group 1
Description SATYAM Customization(3)Data Groups
在功能菜单中选择“安全性-ORACLE-数据组”,打开“数据组”窗⼝
⽤窗⼝菜单“查询-查找”来检索出“标准”数据组。并在其下的明细⾏中新建⼀条记录(具体内容如下),保存后,关闭该窗⼝(如果OA启⽤了MRC,则需要在“MRC”数据组中添加同样⼀条记录)。Application SATYAM CustomizationORACLE ID APPSDescription(4)Request Groups
在功能菜单中选择“安全性-责任-请求”,打开请“求组窗⼝”,创建⼀条新记录。Group SATYAM Concurrent ProgramsApplication SATYAM CustomizationCodeDescription
接下来的步骤是建⽴Menu,Responsibility,Profile等,在此就不再⼀⼀赘述了。Lab 2: 创建客户化数据库对象的注意事项
在开发客制化Form往往要创建的新的数据库对象。⽐如。⽐如⼀个客制化的应⽤为SATYAM,对应的Schema为SATYAM,那么实在对象如table、Sequence、index、triggers等应建在SATYAM Schema的表空间中,其他代码对象如packages,views, procedures, functions等创建在Apps Schema下,常见错误是把表建在Apps Schema下,⼜来建别名,这个时候删除别名时会报对象不存在,⽽建别名的时候⼜报对象已存在。Apps Schema应创建其他⽤户对象的synonyms或者得到授权访问这些对象。
下⾯是⼀段典型创建客户化表,索引,视图等对象的典型代码,可以对⽐验证下是否符合上⼀段所说的原则。PROMPT CONNECT DATABASE...
CONNECT SATYAM/&&SATYAM_PASSWORD@&&SID_NAME;--以SATYAM⽤户登录,创建实在对象PROMPTCREATE TABLE......
DROP TABLE satyam_catalog_group_headers_all;
CREATE TABLE satyam_catalog_group_headers_all( CATALOG_GROUP_HEADER_ID NUMBER not null,CATALOG_GROUP_SERIAL_NUMBER VARCHAR2(30),CATALOG_GROUP_NAME VARCHAR2(100),CATALOG_GROUP_DESCRIPTION VARCHAR2(240),… ,
ORG_ID NUMBER not null,CREATION_DATE DATE not null,CREATED_BY NUMBER not null,LAST_UPDATED_BY NUMBER not null,LAST_UPDATE_DATE DATE not null,LAST_UPDATE_LOGIN NUMBER,ATTRIBUTE_CATEGORY VARCHAR2(30),ATTRIBUTE1 VARCHAR2(150),… ,
ATTRIBUTE15 VARCHAR2(150),REQUEST_ID NUMBER,
PROGRAM_APPLICATION_ID NUMBER,PROGRAM_ID NUMBER,
PROGRAM_UPDATE_DATE DATE) TABLESPACE satyam_data;PROMPT CREATE INDEX......
drop index SATYAM_CATALOG_group_HEADERS_U1;drop index SATYAM_CATALOG_group_HEADERS_U2;
CREATE UNIQUE INDEX SATYAM_CATALOG_group_HEADERS_U1 ON
SATYAM_CATALOG_group_HEADERS_ALL(catalog_group_header_id) TABLESPACESATYAM_IDX;
CREATE UNIQUE INDEX SATYAM_CATALOG_group_HEADERS_U2 ON
SATYAM_CATALOG_group_HEADERS_ALL(org_id,catalog_group_name) TABLESPACESATYAM_IDX;
PROMPT GRANT/REVOKE OBJECT PRIVILEGES --向APPS⽤户授权
GRANT SELECT, INSERT, UPDATE, DELETE, REFERENCES, ALTER, INDEX ON
SATYAM.SATYAM_CATALOG_group_HEADERS_ALL TO APPS WITH GRANT OPTION; GRANT SELECT, INSERT,UPDATE, DELETE, REFERENCES, ALTER, INDEX ON SATYAM.SATYAM_CATALOG_group_lineS_ALL TO APPSWITH GRANT OPTION;
PROMPT CREATE SEQUENCE
DROP SEQUENCE SATYAM_CATALOG_group_HEADERS_S;
CREATE SEQUENCE SATYAM_CATALOG_group_HEADERS_S START WITH 1;PROMPT CONNECT DATABASE......
CONNECT APPS/&&APPS_PASSWORD@&&SID_NAME;--以APPS⽤户登录,创建虚对象PROMPT CREATESYNONYM.....
DROP SYNONYM SATYAM_CATALOG_group_HEADERS_ALL;DROP SYNONYM SATYAM_CATALOG_group_HEADERS_S;
CREATE SYNONYM SATYAM_CATALOG_group_HEADERS_ALL FORSATYAM.SATYAM_CATALOG_group_HEADERS_ALL;
CREATE SYNONYM SATYAM_CATALOG_group_HEADERS_S FORSATYAM.SATYAM_CATALOG_group_HEADERS_S;COMMIT;
PROMPT CREATE OR REPLACE VIEW.....
CREATE OR REPLACE VIEW SATYAM_CATALOG_GROUP_HEADERS ASSELECT
catalog_group_header_id,catalog_group_serial_number,catalog_group_name,catalog_group_description,…,org_id,creation_date,created_by,last_updated_by,last_update_date,last_update_login,attribute_category,attribute1,…,attribute15,request_id,
program_application_id,program_id,
program_update_date
FROM satyam_catalog_group_headers_allWHERE NVL(ORG_ID,
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'), 1, 1),
' ',NULL,
SUBSTRB(USERENV('CLIENT_INFO'), 1, 10))), -99)) =
NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'), 1, 1),' ', NULL,
SUBSTRB(USERENV('CLIENT_INFO'), 1, 10))), -99) ;
CREATE OR REPLACE VIEW SATYAM_CATALOG_GROUP_HEADERS_V ASSELECT
catalog_group_header_id,catalog_group_serial_number,catalog_group_name,catalog_group_description,…,org_id,creation_date,created_by,last_updated_by,last_update_date,last_update_login,attribute_category,attribute1,…,request_id,
program_application_id,program_id,
program_update_date
FROM satyam_catalog_group_headers;
在上段代码中,⾸先建⽴了⼀个satyam_catalog_group_headers_all基表,有⼏个特殊字段ORG_ID(保存OU_ID),五个
Set_Who字段(CREATION_DATE、CREATED_BY、CREATION_DATE、CREATED_BY、LAST_UPDATE_LOGIN)相信⼤家都很熟悉了。ATTRIBUTE字段ATTRIBUTE_CATEGORY、ATTRIBUTE1--ATTRIBUTE15为使⽤弹性域预留;
REQUEST_ID、PROGRAM_APPLICATION_ID、PROGRAM_ID、PROGRAM_UPDATE_DATE是四个并发程序相关的字段,我也没有⽤过。
随后⼜创建了2个View: satyam_catalog_group_headers,
satyam_catalog_group_headers_v,前者的作⽤在于根据OU屏蔽业务数据,不⽤去管那⼀串条件的含义,照抄会⽤即可。后者⽤于关联其它表或视图的数据。
我习惯采⽤的命名⽅式为基表(A):XXXX_ALL;OU的的视图(B):XXXX;关联其它表或视图的视图(C):XXXX_V。
如果要在客户化表上应⽤弹性域或预警,必须注册Tables/Columns,系统AD_DD包⾥有四个procedure经常会⽤到,procedure register_table (p_appl_short_name in varchar2,
p_tab_name in varchar2,p_tab_type in varchar2,
p_next_extent in number default 512,p_pct_free in number default 10,p_pct_used in number default 70);
procedure register_column (p_appl_short_name in varchar2,p_tab_name in varchar2,p_col_name in varchar2,p_col_seq in number,p_col_type in varchar2,p_col_width in number,p_nullable in varchar2,p_translate in varchar2,
p_precision in number default null,p_scale in number default null);
procedure delete_table (p_appl_short_name in varchar2,p_tab_name in varchar2);
procedure delete_column (p_appl_short_name in varchar2,p_tab_name in varchar2,p_col_name in varchar2);eg:
ad_dd.register_table(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’, ’T’,8, 10, 90);
ad_dd.register_column(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’,’A TTRIBUTE_CATEGORY’, 1,’VARCHAR2’, 150, ’Y’, ’N’);
ad_dd.register_column(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’,’A TTRIBUTE1’, 2, ’VARCHAR2’,150, ’Y’, ’N’);…
ad_dd.register_column(’SATYAM’, ’SATYAM_CATALOG_GROUP_HEADERS_ALL’,’A TTRIBUTE15’, 16, ’VARCHAR2’,150, ’Y’, ’N’);Lab 3: 新建Form
修改Template.fmb模板是新建Form最快⽅法,对于在EBS系统中使⽤的Form,利⽤Template 可以省去许多繁杂的⼯作。开发之前记得把服务器上appstand.fmb,pll,fmb⽂件
($AU_TOP/RESOURCE/US路径下)下载到本地指定路径,以防打开或编译fmb⽂件时找不到引⽤对象。在Form builder中打开Template.fmb⽂件后,修改module name后另存为。下⼀步是修改3个地⽅。1 pre-form trigger(form level)
修改此处FND_STANDARD.FORM_INFO的版本号,⽇期,作者
App_window.set_window_position中BLOCKNAME要替换为window name,否则打开Form时会有问题。2 when-new-form-instance trigger(form level)
修改FDRCSID中FMB⽂件名
3 program unit : app_custom.close_window
此处⾄少要制定your first window的名字,否则窗体不是关不掉就是出现红条,其他的代码可以根据程序的需要修改。Template中所有的对象都可以保留,也可以删除重建。我的建议是2个Block(BLOCKNANE和DETAILBLOCK)可以删除,其他对象如WINDOW(BLOCKNAME),CANVAS(BLOCKNAME)等都可以保留,修改对应的属性即可,新建的对象如BLOCK等记得设置正确的PROPERTY CLASS。另外⼏个属性要特别注意下:MODULEProperty NoteFirst navigation data
block 已运⾏Form光标最先进⼊的数据块,建议设置,否则系统会根据BLOCK对象的排列顺序决定First navigation data blockWINDOWProperty Note
Primary Canvas 建议指定CANVASProperty Note
WINDOW 建议指定
这些属性可能由于删除再新建对象发⽣丢失,所以每次开发Form时都需要特别⼩⼼。Lab 4: 布局⽅法
⼀个Form Module可能包含了多个window,多个Data Block,多个canvas,在对象众多的情况下,布局⽅法遵循⼀定的规则可让你事半功倍。
如图4.1,⼀般来说布局,应按照windows-canvases-data block-items的顺序,先简单说明下各对象与布局相关的属性。WindowProperty Note
X Position Form启动时X轴坐标Y Position Form启动时Y轴坐标Width Form实际宽度Height Form实际⾼度CanvasProperty Note
Window 指定Canvas所在的window
View X Position on Canvas 可见部分相对于Canvas 的X轴坐标,通常置0View Y Position on Canvas 可见部分相对于Canvas 的Y轴坐标,通常置0Width Canvas实际宽度Height Canvas实际⾼度
图4.1
Canvas⼀般都要设置以上属性,但是根据Property Class的不同,⼏类canvas还是有所区别。1 Content Canvas
⼜称基础画布或内容画布,就像刷地板要先上底漆⼀样,是必不可少的。⼀般情况下Content Canvas的宽和⾼应设置和windows的宽⾼⼀致或略⼩于,我的建议是设成⼀样。
2 CANV AS_STACKED_FIXED_FIELD Canvas
译作固定的堆叠画布,顾名思义,固定意味着该画布的尺⼨是固定的,千万不要误以为该画布会永远锁定在界⾯的最上层。Property Note
Viewport X Position 可见部分相对于Form源点的X轴坐标,修改该值影响对象在layout窗体上的位置
Viewport Y Position 可见部分相对于Form源点的Y轴坐标,修改该值影响对象在layout窗体上的位置
Viewport Width 可见部分的宽度,在layout窗体上显⽰的宽度,<= Canvas Width Viewport Height 可见部分的⾼度,在layout窗体上显⽰的宽度,参见图4.23 CANV AS_STACKED Canvas
译作堆叠画布。在需要显⽰得列太多,界⾯上⽆法⼀次排开的情况,需要使⽤滚动条时多⽤到这种画布。这种类型的画布,⼀般Canvas的宽和⾼要⽐Viewport的宽和⾼要⼤得多,另外还要设置Show Horizontal Scroll Bar属性为Yes,允许使⽤横向滚动条。下沿间距图4.2
4 Tab_Canvas
译作页签画布,此类画布使⽤起来⽐较简单再此就不再赘述了⽐较简单,与之相关的触发器我会在后⾯的章节总结。关于调整布局的⼀些技巧
在Form builder中选Format--Layout Option—Ruler,弹出4.3窗体,Units选Character Cells,Character Cell Size不⽤管它,不要修改;Grid Space建议选1或0.5,Number of Snap Points Per Grid Spacing建议选1;具体含义是采⽤Character Cells⽅式进⾏位置调整,Grid Space表⽰每个Grid的幅度,Number of Snap Points Per Grid Spacing我的理解是⿏标选中后⽅向键调整多少次才能达到⼀个Grid Space的幅度。所以Grid Space越⼤,Number of Snap Points Per Grid Spacing 越⼩表明每次调整的幅度越⼤,设成上⾯的数字是个⼈的经验,可据需要⽽定,很多时候我都是修改对象坐标属性,然后利⽤对齐功能来调位置的。
图4.3
对于CANV AS_STACKED CANV AS来说,⼀般都会使⽤横向滚动条,注意在Units选择Character Cells时(如图4.3),应设置Viewport的下沿的Y坐标⽐⽐该画布上位置最低Item下沿Y座标还要低10-12个刻度,如图4.2所⽰下沿间距。
对于复杂的界⾯,可能同时使⽤4种类型的Canvas,甚⾄Tab Canvas上还放置了堆叠画布,整个显得⾮常Layout界⾯复杂⽽混乱,此时可在使⽤Form builder中View菜单下的3个选项:1Show Canvas ;2 Show Views;3 Stacked Canvas。选中Show Canvas会显⽰当前Canvas的实际⼤⼩的框架(width和height),并显⽰底⾊;选中Show Views会显⽰当前Canvas的Viewport 的框架(Viewport width和Viewport height),此选项的效果在对象为堆叠画布时显得⾮常直观;Stack Canvas选项在画布互相堆叠时尤为好⽤,但是使⽤时⼀定要注意技巧。
图4.4
假如在⼀个基础画布上要显⽰固定堆叠画布SKN_HEADER(A),页签画布SKN_TAB_CANV AS(B)和另⼀个固定堆叠画布BLUECARD_STACKED_CANV AS(C),想把A 放在最底层,B次之,C在最上⾯。那么在图4.4的界⾯上必须按住CTRL键,依次先选中A 再选中B最后选中C,注意次序不能有任何的颠倒,否则点OK按钮后会发现有些Canvas看不见了。操作的原则是:先选择最底层,再选择此底层,最后选择最上层。当然你分步完成上述调整也没问题,但是⼀定要确保选择次序符合上述原则。Block
注意设置Number of Records Dispalyed和相关的Scrollbar属性。Item
Item和布局属性相关的属性有Property Class和相关prompt属性。我⼀般的做法是根据不同类型字段先批量修改PropertyClass和Format mask,第⼆步根据Block的设置批量设置ITEM 的相关prompt属性如:Property Note
width ⽇期类型1.2,时间类型.8,⽇期加时间1.7,按钮列⼀般为1.2 height 选中Property Class⼀般不许修改,button为.3,其他为.25
prompt justification Right(数字型字段),其他选Start
prompt attachment edge 单⾏块⼀般选Start,单⾏块⼀般选Topprompt attachment offset .025prompt alignment offset .025
每个ITEM的prompt的属性值,我会在layout 窗⼝中⼀个个修改。Navigate 相关
⽆论是Block还是Item都有⼀些与导航相关的属性,决定了界⾯上按tab键,光标的移动⽅式。BlockProperty Note
Navigate Style Same Record/Change Record/ Change Data Block
Previous Navigation Data Block ⽐如关闭⼀个window后,光标会落到Previous Block上的第⼀个可导航Item时
Next Navigation Data Block 当导航⽅式为Change Data Block时,光标落在前⼀个块最后⼀个可导航Item时,再按tab键,光标就会跳到Next Block上
当Previous Navigation Data Block,Next Navigation Data Block属性为空时,系统会⾃动根据Form Builder对象树各block的排列顺序,⾃动决定各Block的导航顺序。这种默认的⽅式可能会带来其他的问题,⽐如关闭窗体时,光标落在不该落到的块上,最好指定⼀下。Item
Property Note
Keyboard Navigable 设置Item是否可使⽤tab键导航
Previous Navigation Item 前⼀个可导航Item(可能会向前导航到前⼀个块的最后⼀个可导航Item)
Next Navigation Item 后⼀个可导航Item时(可能会向后导航到后⼀个块的第⼀个可导航Item)
注意当Previous Navigation Item,Next Navigation Item属性为空时,系统会⾃动根据Form Builder对象树各block下Item的排列顺序,⾃动决定各Item的导航顺序。所以当我们不想去设置Item的导航顺序,⼜希望界⾯上各字段能够按照从上到下从左到右的顺序导航,那么就必须合理安排各Item的排列顺序,切记。Lab 5: 赋初始值&Item 对象相关属性
本节介绍⼀下Form各对象赋初始值的⽅法以及设置Item属性需要注意的地⽅。1.赋初始值
在form中需要记录⼀些变量的值,这些值在Form的整个运⾏过程中都可能被引⽤,为了避免重复的获取,我们采⽤的策略为在Form的when-new-form-instance时间中将这些变量读取出来赋值给parameter(有时候是存⼊global变量中),以后对这些变量的引⽤就转换成对Form parameter的引⽤。以下是程序⽰例:PROCEDURE get_profile_parameterISBEGIN
Fnd_profile.GET('ORG_ID',:PARAMETER.G_ORG_ID);Fnd_profile.GET('USER_ID',:parameter.G_USER_ID);
select user_name into :parameter.G_USER_NAME from fnd_userwhere user_id = :parameter.G_USER_ID ;
select NVL(employee_id,147) into :parameter.G_EMPLOYEE_IDfrom fnd_user where user_id = :parameter.G_USER_ID ;END get_profile_parameter;
对于Item来说也需要赋初始值,设置⽇期型字段的初始值为当前⽇期,某主键字段为sequence的nextvalEg: Data Type Initial Value NoteDate $$DATE$$ 当前⽇期
NUMBER :sequence.order_header_s.nextval 取⾃sequenceNUMBER fnd_ profile.value(‘org_id’) 取⾃系统变量Char :parameter.G_USER_NAME 取⾃parameter2.Item属性
各种Item由于property class的不同视觉效果与属性设置都有所不同,下⾯简单列举各类Item属性设置需要注意的地⽅。1.Text Items
可以根据需要为Text Items设置不同的Property Class,TEXT_ITEM,
TEXT_ITEM_DISPLAY_ONLY,TEXT_ITEM_MULTILINE,TEXT_ITEM_DATE ,
REA TION_OR_LAST_UPDATE_DA TE,TEXT_ITEM_PERCENT_FIXED等。对于各种类型的字段,注意根据data type设置合适的Format mask。2. Display Items
应当设置⾜够宽以显⽰数据库中该项的最长内容。3. Check Boxes需要特别注意的属性Property NoteValue when checkedValue when unchecked
Check Box mapping of other values checked/ unchecked/not allowed,当该选项为notallowed时,必须指定Initial value,否则编译出错。
还有⼀类⽐较特殊的Check Boxes(property class-CHECKBOX_COORDINA TION),⽤以控制Master-Detail Block的关联⾏为,在随后第6章习会再次提及。4. Buttons
可设置Access Key。(ALT+KEY)5. Option Groups必须设定缺省值。6. Poplists
在可选值不超过15个情况下,Poplists是个不错的选择,对Poplists的编程应对应List Item Value。应尽量避免将Poplists设为multi-row blocks的⾸字段。Property Note
List Style 列表风格,poplist/combox/tlist使⽤前两种风格居多,在设置poplist风格的Elements时,有⼏项就输⼊⼏项,⿏标不要做多余的操作,⽐如明明只有4项,偏要点到第5项上去,虽然什么都不输⼊,编译后列表就是多出⽩花花的⼀⾏,⾮常的难看,此时只有删除该列表Item,重头来过,甚是⿇烦。
mapping of other values 不指定的话遇到该项数据不匹配的情况,可能整条记录都被屏蔽。Initial value 不指定的话编译时会提⽰,不影响程序。7. LOVS
LOV可以⽐Poplists容纳更多的记录,使⽤中需注意的地⽅在后续章节介绍。8. Flexfields
关于Key弹性域和Descriptive弹性域的介绍,再后续的⼀章中有专门的说明。9. Current Record Indicator
建议对于多⾏的Block都建⽴⼀个Property Class为CURRENT_RECORD_INDICATOR的⾮数据字段,作⽤:当前记录指⽰器。
10. Rowid
这个字段不是必需但很有⽤。Property Class:ROW_ID,当需要利⽤记录的物理地址更新或删除⼀条记录时,该字段就是必需的,当然前提是Block的数据源中必须包含该字段。Lab 6: Compute Data
操作数据⽆⾮删除、更新、插⼊的动作,在Oracle Form依靠下⾯触发器完成这些操作。Form Trigger
Key-commit 保存按钮触发/Ctrl+S 键盘触发/代码触发Block Trigger
Pre-delete Pre-update Pre-insertOn-delete On-update On-insert On-lock
Oracle保存数据的机制:第⼀步依次从数据库中删除在Block上删除的记录(删除每条记录时先触发Pre-delete,再触发On-delete),第⼆步依次从数据库中更新在Block上更新的记录(更新每条记录时先触发Pre-update,再触发On-update),第三步依次向数据库中插⼊在Block 上插⼊的记录(插⼊每条记录时先触发Pre- insert,再触发On- insert),第四步COMMIT。其中任何⼀步出错的话都会引发异常,回滚以前的操作。所以我猜想Oracle Form肯定有⼀套机制能记录Block上每条记录的状态,所以在保存时可触发相应的Trigger。
如果Form在⼀定条件下才允许保存,可以将控制逻辑写在Form级trigger pre-commit 或者block级的Pre-delete,Pre-update,Pre-insert代码中(这种情况控制可以针对操作类型进⾏控制,更加精细),⼀但条件不满⾜,⽴即抛出异常或忽略本次操作。
On-lock触发器在⽤户在界⾯上企图修改数据时触发,利⽤了Oracle悲观封锁的机制,保证数据的完整性和⼀致性,关于这种机制的详细说明,可参考我另⼀篇⽇志。
http://www.doczj.com/doc/50ed11630b1c59eef8c7b4c4.html /2743/viewspace-2332.html在需要设置who字段的block中,Pre-insert和Pre-update都要添加如下代码:fnd_standard.set_who; oracle会调⽤API给那⼏个who字段赋值,不⽤我们操⼼了。
另外在pre-insert的触发器中设置某些Item 的初始值,⽐如说给org_id字段赋值,⽐如给⼀个主键Item赋⼀个
sequence.nextval,这样与在Item的initial value属性中设定sequence.nextval 相⽐可以节省很多序列号,也可以更好保证序列号的连续性,这在某些业务中是⾮常关键的。Eg:
:BLOCKNAME.ITEM_NAME := FND_PROFILE.VALUE(‘V ALUE_NAME’);SELECT SEQUENCE_A.NEXTVAL INTO :BLOCKNAME.ITEM_NAME ;
操作数据前提是设置Block的Insert_Allowed,Update_Allowed,Delete_Allowed属性为Yes,对于数据源为基表,单表视图或简单多表视图,可以利⽤Oracle Form⾃⾝的机制,不需借助任何代码,可以实现数据的操作。1.操作基表和单表视图Block
Property Value NoteDML Data Target Type Table2.操作多表视图Block
Property Value NoteDML Data Target Type Table
DML Data Target Name Table_Name 操作的基表名
Item(主键字段)Property Value Note
Primary Key Yes 不指定主键的话编译会出错
以上2种情况,系统会⾃动构造Insert,Update,Delete,Lock语句。根据我Trace的结果来看,Update,Delete,Lock都是根据记录的ROWID来唯⼀确认⼀条记录的。3.使⽤代码操作数据
对于数据的操作,我都建议⽤代码实现,这样⽐较直观,出了问题也容易找到原因。Oracle Form会分别调⽤ON-INSERT触发,ON-UPDA TE触发,ON-DELETE触发分别完成插⼊、更新、删除操作。需要注意的是:在符合上⾯1,2类型的情况下,创建了ON-XXXXXX触发器,都⾥⾯的代码为NULL;这时候系统就不能⾃动操作数据了,因为Oracle Form检测到了ON-XXXXXX 触发器,执⾏了其中的代码,即执⾏了NULL;语句。PROCEDURE INSERT_ROW ISCURSOR c is
select return_fix_headers_id
from TVSN_RETURN_FIX_HEADERS_ALL
where return_fix_headers_id = :HEADER.return_fix_headers_id ;BEGIN
insert into TVSN_RETURN_FIX_HEADERS_ALL(return_fix_headers_id,line_type,order_header_id,f_to_r_flag,ORG_ID,creation_date,…
,attribute15 )values
(:HEADER.return_fix_headers_id,:HEADER.line_type,:HEADER.order_header_id,:HEADER.f_to_r_flag,:PARAMETER.G_ORG_ID,:HEADER.creation_date,…
,:HEADER.attribute15 );open c ;
fetch c into :HEADER.return_fix_headers_id ;
if (c%notfound) thenclose c ;
raise no_data_found ;end if ;close c ;
END INSERT_ROW;
PROCEDURE UPDATE_ROW isBEGIN
update TVSN_RETURN_FIX_HEADERS_ALLset
return_fix_headers_id = :HEADER.return_fix_headers_id,line_type = :HEADER.line_type
,order_header_id = :HEADER.order_header_id,f_to_r_flag = :HEADER.f_to_r_flag
,org_id = :http://www.doczj.com/doc/50ed11630b1c59eef8c7b4c4.html _ID,creation_date = :HEADER.creation_date,…
,attribute15 = :HEADER.attribute15
where return_fix_headers_id = :HEADER.return_fix_headers_id ;if (sql%notfound) thenraise no_data_found ;end if ;
END UPDA TE_ROW;procedure DELETE_ROW isbegin
delete from TVSN_RETURN_FIX_HEADERS_ALL
where return_fix_headers_id = :HEADER.return_fix_headers_id;if (sql%notfound) thenraise no_data_found;end if;
end DELETE_ROW;
以上3段代码是很经典的插⼊,修改,删除语句。特点是每次操作完成之后都有⼀个检验过程,红⾊粗体语句。可以将这些代码放在数据库,pll⽂件,form的program unit,甚⾄是直接写在trigger 中,从性能的⾓度出发可能放在数据库中更⾼效,综合维护⽅便等因素考虑,我建议写在orm的program unit中。4.⼀个常见的错误
图6.1
有时候在输⼊⼀条新记录保存后再修改,会发⽣上图所⽰错误。错误的原因是ON-LOCK 触发器检查到界⾯数据和后台数据不⼀致,所以提⽰⽤户。看到第3节PROCEDURE INSERT_ROW代码::PARAMETER.G_ORG_ID。系统向数据库中插⼊了⼀个参数的值,但是并没有保证界⾯上的字段:http://www.doczj.com/doc/50ed11630b1c59eef8c7b4c4.html _ID有值或者:http://www.doczj.com/doc/50ed11630b1c59eef8c7b4c4.html _ID的值与:PARAMETER.G_ORG_ID⼀致。
⼀般导致这种错误的原因有:1界⾯字段未赋值使⽤了替代值存⼊数据库;2由于Item字段长度不够导致部分数据截断;3值列表与View字段内容不⼀致。建议在Lock Row字段中只锁定界⾯Block上的可见字段,在操作数据前保证Block字段与传⼊参数保持⼀致,最后就使⽤Block字段作为传⼊参数。5.Master-Detail Block 的级联删除
创建relation建⽴主从块关系时,可以设置级联删除属性,如图6.2所⽰,确定后主块会⾃动⽣成Pre-delete tigger代码为删除在数据库中对应的detail记录
图6.2
如果在创建relation为设置级联删除,可以通过修改relation的属性Delete Record Behavior 为Cascading(如图6.3),新建主块Pre-delete trigger编写删除detial记录的代码,也能达到同样的效果。
图6.3
通过这种机制,oracle Form实现了前台界⾯清除数据和后台数据库删除数据的⼀致。有两点需要注意的是:
1当Detail Block的的数据源是View时,⾃动⽣成的Master Block Pre-delete trigger代码的删除对象是View,记得改成基表。2 当三层Master-Datail关系: C is child of B is child of A,当删除A块记录调⽤pre-delete代码删除B块记录,但是不会⾃动调⽤B块pre-delete代码删除C块记录,所以要将删除C块记录的语句写在A块pre-delete中。Lab 7: Query Data
F11查询功能是Oracle ERP的⼀⼤亮点,只要属性设置正确,⼏乎界⾯上的所有字段都能做为查询字段(Query_Allowed =Yes)。我们使⽤Ctrl+F11执⾏查询,实际上是执⾏
do_key(execute_query)语句,系统会在查询语句中⾃动添加Block的WHERE Clause和ORDER BY Clause,这⼀点可以通过帮助--检查http://www.doczj.com/doc/50ed11630b1c59eef8c7b4c4.html ST_QUERY加以验证。
另外在很多情况下,都要通过代码实现查询功能,⽐如打开Form时执⾏查询,点击按钮查询,保存后重新执⾏查询,某些栏位值发⽣变化后执⾏查询等查询代码实现:
1 GO_BLOCK(‘BLOCKNAME’);
2 SET_BLOCK_PROPERTY(‘BLOCKNAME’,DFAULT_WHERE,’where clause’);
3 DO_KEY(‘EXECUTE_QUERY’); oracle建议⽤do_key(‘execute_query’)取代execute_query在实际应⽤中,往往需要根据业务逻辑创建⼀个parameter,控制是否执⾏查询。常见的实现查询功能的触发器有:when-new-form-instance,when-button-pressed,when-new-item-instance等。在某些触发器中查询功能是禁⽤的,如when-validate-item等。
除了F11查询功能之外,EBS还⽀持⼀种更加灵活的查询⽅式—QUERY FIND,即我们通常所说的⼿电筒查询。这是⼀种Non-Query模式下的查询,主要要两种模式1Row-LOV和2Find Window,所有Query Allowed属性为Yes的Block都能⽀持这两种⽅式的任意⼀种。1. Row-LOV
Row-LOV类似与普通LOV,在下列情况下适于使⽤:(1) ⼀次只返回⼀条记录,特别适合single row block;(2) 在detail block中⾃动查询关联master block的所有记录;(3) 基于主键⽽不是其他属性查询记录;(4) 数据量很少的情况。
⽐如我想建⽴⼀个关于order_line_num的lov,根据选中的订单⾏号码查询详细信息,以下是创建Row-LOV的步骤:(1)⾸先创建⼀个parameter(ORDER_LINE_NUM_QF)如⽤于存储返回的PrimaryKey(order_line_num);
(2)接着创建合适的Row-LOV设置order_line_num字段返回给:parameter.ORDER_LINE_NUM_QF
(3)再创建block-level PRE-QUERY trigger (Execution Hierarch:Before)IF :parameter.G_query_find = 'TRUE' THEN
:lines.order_line_num := :parameter.ORDER_LINE_NUM_QF;:parameter.G_query_find := 'FALSE';end if;
(4) 最后创建block-level QUERY_FIND trigger (Execution Hierarch:Override)app_find.query_find('ORDER_LINES_QF');2. Find window
Find window在下列情况下适于使⽤:
(1) ⼀次返回多条记录,特别适合multi-row block;(2) 需要多个查询条件关联进⾏复杂查询的;(3) 数据量很多的情况。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuo7.cn 版权所有 湘ICP备2022005869号-9
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务