搜索

RSS

RSS Link

再谈 Formsville

2010年11月13日 01:34 | Comments(0) | Category:Oracle-EBS-FORM | Tags:

 

文章来源:http://www.oracle.com/technology/global/cn/pub/articles/muir-forms.html

作者:Penny Cookson 和 Chris Muir

了解许多基于 Oracle Forms 的应用程序常用的主要体系结构概念,仍然是十分重要的。

2008 年 6 月发表

新程序员在转到运行传统 Oracle Forms 和 Oracle Designer 应用程序的 Oracle 站点时,将面临许多挑战。新程序员在很大程度上将依赖于高级程序员来教他们如何构建应用程序的结构,以及从来之不易的经验中所了解到的此类结构的优点。当然,围绕 Oracle Forms 确实存在着大量的最佳实践,而且 Oracle Designer 也为 Oracle Forms 体系结构提供了独特的设计方法。但是在许多站点,高级程序员已进一步深入其研发工作,留下他们的后续程序员猜测为何要将应用程序构建为目前的结构。随着不断增加的维护问题的修复压力,原有解决方案的简洁性可能会受到损害。

对于初次接触 Oracle Forms 的人员,本文将尝试解释许多基于 Oracle Forms 和 Oracle Designer 的应用程序常用的主要体系结构概念,包括其优点以及如何善用之。本文特别着重介绍以下内容:

  • 简单的 Oracle Forms 触发器体系结构
  • 灵活的公司-应用程序-表单 Oracle Forms 库体系结构
  • 将方法扩展到从 Oracle Designer 生成的表单
  • Oracle Designer 构件,包括表 API、cg_ref_codes 和日志表。

简单的 Oracle Forms 触发器体系结构

不同的开发人员在处理不同问题时,会提出同样的最佳实践解决方案,这在计算中常常是令人惊讶的事情。有时,这来自于难得的经验、巧妙的编程,或只是来自积极倾听 Oracle 或者 Oracle 用户群活动上讨论的最佳实践。Oracle Forms 应用领域内出现的此类常用解决方案正是我们所谓的 Oracle Forms 触发器框架。从 Oracle Forms 4.5 版本直到最新版本,此编程框架都已证明十分有用,并且可在许多 Oracle 站点的不同混合表单中找到。

在本部分中,我们将描述触发器体系结构及其构建方式以及其主要的优缺点。对于熟悉 Oracle Headstart 的人员,这两种方法之间存在明确的相似性。

注:Oracle Headstart 是与 Oracle JHeadstart(“Java”Headstart)目的相同的产品,前者仅针对 Oracle Designer 和 Oracle Forms,而后者则支持 Oracle JDeveloper 和 Oracle 应用开发框架 (Oracle ADF)。Oracle Headstart 曾经一度是许多进行“100% 生成”的 Oracle 站点的选择,并且是当时从 Oracle Designer 生成 Oracle Forms 应用程序的最佳实践技术。

目标

触发器框架的目标包括三个方面:

• 删除“触发器四处散落的碎片”— 为了避免前一程序员在项级、块级和表单级编码出数以百万计的触发器的可怕情景,这些触发器是非常难以调试和搜索的。

• 增加触发器类型和触发时间的透明度,而不必特别记住每个触发器类型的独特性,这对于不熟悉 Oracle Forms 但分配了维护现有 Oracle Forms 系统任务的初级程序员来说尤为重要。

• 通过更简便的调试来降低维护成本 — 归因于 Oracle Forms 调试器在多个版本中都无法使用的广为人知的事实。由于我在使用不同版本 Oracle Forms 的客户站点之间移动,我发现这个优势特别重要。

实施框架

此框架有两个规则:

• 无项级触发器,所有项级触发器均已提升为块级。

• 除了调用程序单元的触发器外,触发器中无代码。

第一个规则意味着不应编写项级触发器,必须在块级执行此工作。第二个规则意味着块级或表单级触发器将调用传入触发器/事件名称的程序单元,定义如下所示。

此框架要求

• 每个块有一个程序包 — 至少每个包含触发器代码的块都有一个程序包。

• 一个表单有一个单独的程序包 — 至少包含触发器代码的表单有一个程序包。

例如,对于具有两个块 ORGANISATIONS_BLK 和 EVENTS_BLK 的表单,表单的触发器代码和两个块将需要三个程序包,即 FORMS_PKG、ORGANISATIONS_BLK_PKG 和 EVENTS_BLK_PKG。

触发器的定义如下所示:

在表单级或块级定义的任何触发器的格式应如下所示 — 注意上述的两个框架规则,我们不定义项级触发器:

FORM.WHEN-NEW-FORM-INSTANCE 触发器:
    form_pkg.event_handler('WHEN-NEW-FORM-INSTANCE');
 
BOOKINGS_BLK.WHEN-VALIDATE-ITEM 触发器:
    bookings_blk_pkg.event_handler('WHEN-VALIDATE-ITEM');

注意每个程序包如何带有 event_handler 方法,我们将调用此方法传入已触发的触发器名称,这将引导我们进入下一讨论点:

每个程序包都要求:

• 采用以下规范的单个方法处理程序:

PROCEDURE event_handler(i_trigger IN VARCHAR2) IS ....

现在 event_handler 中将有一个大型 IF-ELSIF-ELSE-THEN(或 CASE)语句,具体取决于 Oracle Forms 中的 PL/SQL 版本,该语句用于确定已触发了哪个触发器或要调用哪个其他过程来处理事件。

从潜在意义来讲,如果触发器是 event_handler 过程中的项级触发器,则还需派生出 :system.cursor_item 以了解哪个项触发了触发器,从而确定要执行的代码。

让我们举个例子来帮助解释上面的内容。

假设在 BOOKINGS_BLK 中,对于 STATUS 和 NUMBER_ATTENDEES 这两项,我们希望基于 WHEN-VALIDATE-ITEM 触发器在这些字段上编写代码以执行数据验证。

考虑到上文解释的触发器框架,我们不会为每项创建两个单独的 WHEN-VALIDATE-ITEM 触发器,相反我们将在 BOOKINGS_BLK 级创建一个 WHEN-VALIDATE-ITEM 触发器:

bookings_blk_pkg.event_handler('WHEN-VALIDATE-ITEM');

event_handler 例程如下所示:

PACKAGE BODY bookings_blk_pkg IS
  PROCEDURE event_handler(i_trigger IN VARCHAR2) IS
    k_trigger_item CONSTANT VARCHAR2(500) := :system.cursor_item;
  BEGIN
    IF i_trigger = 'POST-QUERY' THEN
       ...do something;
    ELSIF i_trigger = 'WHEN-VALIDATE-ITEM' THEN
      IF k_trigger_item = 'BOOKINGS_BLK.STATUS' THEN
         ...do something for status;
      ELSIF k_trigger_item = 'BOOKINGS_BLK.NUMBER_ATTENDEES' THEN
         ...do something for number_attendees;
      ELSE
        NULL; -- Do nothing; other items have no validation
      END IF;
    ELSE
      -- unhandled trigger; programmer error
      RAISE FORM_TRIGGER_FAILURE;    
    END IF;
  END event_handler;
END bookings_blk_pkg;

从以上代码可知,处理 STATUS 或 NUMBER_ATTENDEES 字段时,通常需要进行一个过程调用,除非有少量代码。任何此类过程将保留在同一程序包 BOOKINGS_BLK_PKG 中,以便对代码进行逻辑分组。

调试的优势

除了将用户相似的代码分组到程序包的逻辑优势之外,还可以利用此方法的以下优势:

• event_handler 充当所有触发器事件的阻塞点。无论您使用 Oracle Forms 调试器还是功能良好的旧式内置的消息 Forms,均可将一个断点或对消息的调用置于 event_handler 过程的开头,以确定触发器和触发时间。这对初级程序员尤其有用,因为它使触发器的触发顺序立刻明显起来。

• 如果表单出现故障且您怀疑这是由于多个触发器的交互所致,只需在 event_handler 过程中的触发器勾子前面加上注释符号,即可有效禁用代码。将这种情况与不同表单的项级上有众多 WHEN-VALIDATE-ITEM 触发器的情况进行比较;使用注释符号来禁用所有触发器是件非常困难的任务,尤其是在漏掉一个触发器的时候。

缺点

此方法存在下列缺点:

• 如果删除对 event_handler 的调用,则还需删除 event_handler 代码。否则,代码中将保留一个挂起的冗余事件处理程序。

• 此代码的性能会受到影响,因为它需要一个过程调用,然后需要在 event_handler 过程中遍历一个(可能)非常庞大的 IF-ELSIF-ELSE-THEN 结构。此外,对于某些项,触发器处理程序中实际上不存在对特定项执行的代码,从而导致不必要的处理。

灵活的公司-应用程序-表单 Oracle Forms 库体系结构

上述方法允许您使用标准的事件处理程序机制来添加表单特定代码,但对于需要在应用程序中的多个表单之间甚至组织的多个应用程序之间实现标准化的代码来讲,该怎么办呢?为了实现此目标,我们将使用 PL/SQL 库。

目标

我们希望所有表单以类似方式工作。我们希望触发器在默认情况下执行标准公司代码。此外,如有必要,我们还希望允许 Oracle Forms 在某一特定应用程序中将应用程序级的代码添加至公司代码或改写公司代码。

实施框架

此框架需要以下结构:

• 每个表单都附加一个应用程序 PL/SQL 库。

• 每个应用程序 PL/SQL 库都附加一个公司 PL/SQL 库。

• 在所有表单中都定义一组标准的表单级触发器。每个触发器都将调用应用程序级事件处理程序。

• 默认情况下,应用程序库中的事件处理程序将调用公司库中的事件处理程序。

例如,让我们考虑需要以下 Oracle Forms 行为的组织:

• 进入任何表单的正常行为是执行查询。

• 对于登记应用程序,我们还希望执行一些其他代码来定义进入登记表单的权限。

• 我们希望禁用所有表单的编辑键。

考虑表单 BK0010.fmb。该表单有一个附加的应用程序库 APPPLSTD.PLL,而该应用程序库也相应地有一个附加的公司级库 COMPLSTD.PLL。

BK0010 表单中的 WHEN-NEW-FORM-INSTANCE 触发器包含以下代码:

-- perform normal application processing
app_event_handler_pkg.handle_event('WHEN-NEW-FORM-INSTANCE');

APPPLSTD.PLL 中的 app_event_handler_pkg 程序包将包含以下代码:

PACKAGE BODY app_event_handler_pkg IS
  PROCEDURE handle_event(i_trigger IN VARCHAR2) IS
  BEGIN
    IF i_trigger = 'WHEN-NEW-FORM-INSTANCE' THEN
      -- call an application specific procedure
      security_handler_pkg.set_block_privs;
      -- now call the standard corporate processing
      com_event_handler_pkg.handle_event(i_trigger);
    ELSIF i_trigger = 'KEY-EDIT' THEN
      -- no application specific code just call the corporate code
      com_event_handler_pkg.handle_event(i_trigger);
    ELSIF i_trigger = 'PRE-FORM' THEN
      -- no application specific code just call the corporate code
      com_event_handler_pkg.handle_event(i_trigger);
    ..... trap other standard trigger events
    .........................................
ELSE
      -- unhandled trigger; programmer error     
      RAISE FORM_TRIGGER_FAILURE;   
    END IF;
  END handle_event;
END app_event_handler_pkg;

注意,security_handler_pkg 程序包是特定于应用程序的,并且将在应用程序库 APPPLSTD.PLL 中定义。

COMPLSTD.PLL 中的 app_event_handler_pkg 程序包将包括以下代码:

PACKAGE BODY com_event_handler_pkg IS
  PROCEDURE handle_event(i_trigger IN VARCHAR2) IS
  BEGIN
    IF i_trigger = 'WHEN-NEW-FORM-INSTANCE' THEN
      -- execute the corporate processing
      initialise_pkg.perform_query;     
    ELSIF i_trigger = 'KEY-EDIT' THEN
      -- disable the normal key-edit behaviour
      NULL;
    ELSIF i_trigger = 'PRE-FORM' THEN
      -- no corporate code for this trigger event;
      NULL;
    ..... trap other standard trigger events
    .........................................
    ELSE
      -- unhandled trigger; programmer error
      RAISE FORM_TRIGGER_FAILURE;    
    END IF;
  END handle_event;
END com_event_handler_pkg;

请注意,initialise_pkg 程序包是特定于公司的,而且将在公司库 COMPLSTD.PLL 中定义。

优势

此方法提供了以下优势:

• 标准化。此方法的主要优势是对组织或应用程序中的 Oracle Forms 处理进行标准化的能力。如果已设置模板表单来使用此触发器/库结构,则从模板创建的任何表单都会预先内置此标准处理,而无需开发人员采取任何措施。

• 维护。 此方法还更便于将代码添加到整个应用程序或组织。添加到应用程序或公司库中的事件处理程序的任何代码将立即应用于所有表单。这比打开每个表单并添加其他代码要容易得多。

缺点

此方法存在下列缺点:

• 对应用程序或公司库进行的任何更改都可能会对大量表单产生重大影响。部署前要谨慎控制和严格测试对这些库所做的更改,这一点至关重要。

• 开发人员可以通过在表单的块级或项级创建触发器来改写标准处理。这是此方法的一个预设特性,如上例所示,我们希望为表单中的特定项启用 KEY-EDIT 功能。然而在某些情况下,这可能导致意外改写公司或应用程序处理。开发人员理解此类结构并谨慎定义触发器的执行层次非常重要。

• 程序单元的命名十分重要。在编译时,Oracle Forms 将首先在表单中查找程序单元,然后按程序单元在 Application Navigator 中的显示顺序在附加的库中进行查找,最后会在数据库中进行查找。确保库层次结构中的不同级别上的程序单元具有不同的名称,以防止冲突。

将方法扩展到从 Oracle Designer 生成的表单

Oracle Designer 曾是许多 Oracle 站点选择的 CASE 工具,但如今已让位于相对现代的软件开发工具(如 JDeveloper)。从 Oracle Designer 生成表单时,许多开发人员的目的是实现“100% 生成”,或者至少是最大限度地减少对表单所做的生成后更改。(实现 100% 生成曾一度成为 Oracle Designer 和 Oracle Forms 开发梦寐以求的目标。)实现此目的的一个辅助方法是将上述库结构扩展为一个三层结构,其中每个表单都具有自己的附加 PL/SQL 库。此库包括表单特定代码。

实施

考虑上例中所述的 BLK0010 表单。使用三层结构,该表单将具有自己的附加库 BK0010.PLL。该库包含

• 一个模块事件处理程序,类似于上文为应用程序描述的事件处理程序。

• 特定于表单模块的所有代码。

• 对应用程序级事件处理程序的调用。

BK0010 表单中的 WHEN-NEW-FORM-INSTANCE 触发器包含以下代码:

-- perform module specific processing
mod_event_handler_pkg.handle_event('WHEN-NEW-FORM-INSTANCE');

BK0010.PLL 中的 mod_event_handler_pkg 程序包包括以下代码:

PACKAGE BODY mod_event_handler_pkg IS
  PROCEDURE handle_event(i_trigger IN VARCHAR2) IS
  BEGIN
    IF i_trigger = 'WHEN-NEW-FORM-INSTANCE' THEN
      -- call a module specific procedure
      form_pkg.event_handler('WHEN-NEW-FORM-INSTANCE');
      -- now call the standard application processing
      app_event_handler_pkg.handle_event(i_trigger);    
    ELSIF i_trigger = 'KEY-EDIT' THEN
      -- no module specific code just call the application code
      app_event_handler_pkg.handle_event(i_trigger);
    ..... trap other standard trigger events
    .........................................
    ELSE
      -- unhandled trigger; programmer error
      RAISE FORM_TRIGGER_FAILURE;    
    END IF;
  END handle_event;
END mod_event_handler_pkg;

本文第一部分中所述的程序包(例如,form_pkg)将保留在表单特定库 BK0010.PLL 中,而非表单本身中。

优势

使用此结构的主要优势是可以将代码更改有效地外部化。由于表单仅包含“勾子”而非代码本身,因此几乎不需要更改表单模块。相反,可以在附加到表单的模块特定库中添加和更改代码。

缺点

此方法的管理工作有些繁琐,因为它可能导致大量的 PL/SQL 库,最终达到一个表单一个库。

Oracle Designer 构件

Oracle Designer 还提供了许多实用程序来协助开发过程。其中包括以下内容:

管理引用代码

将引用数据的有效值作为域输入 Oracle Designer 中。然后,可以使用这些域生成引用代码表。根据在 Oracle Designer 中选择的首选项,将表命名为 CG_REF_CODES 或 APP_REF_CODES(其中 APP 是应用程序的缩写)。该表可用于有效值或有效范围。

此表的结构如下所示:

列名

用途

示例 1

示例 2

RV_DOMAIN

代码的类型

COUNTRY

WORKING_DAYS

RV_LOW_VALUE

有效范围的下限值,或有效值

AUS

0

RV_HIGH_VALUE

上限值(如果有的话)

 

7

RV_ABBREVIATION

生成的代码中使用的缩写

A

 

RV_MEANING

有效值说明

Australia

 

在示例 1 中,您将看到 COUNTRY 域的若干行。这些行将用于验证和填充弹出式列表。

示例 2 用于生成表单验证,确保 WORKING_DAYS 域中的任一列的值介于 0 和 7 之间。

API 和触发器

使用 Oracle Designer 开发的应用程序可以针对每个表包括一组程序包和触发器。在以下描述中,名为 ORGANISATIONS 的表用于所有示例中。

表 API

表程序包名为 cg$table_name(例如,CG$ORGANISATIONS),对于每个表,它包括以下结构:

类型

cg$row_type

一个记录,包括数据类型和 ORGANISATIONS 中的每一列相匹配的一组字段,此外还包括其他日志记录字段和行标识字段。

cg$ind_type

一个记录,包含表中每一列的布尔字段。这些指示符用于记录是否在 DML 语句中提供了列的值。

cg$pk_type

一个记录,其结构与表的主键相匹配。

cg$table_type

ORGANISATIONS 记录表。

cg$tableind_type

cg$ind_type 表。

可以使用提供的过程执行所有标准 DML 操作和一些其他验证。

过程

ins

将记录插入表中。
接受类型为 cg$row_type 的参数 cg$rec,该参数包含要插入 ORGANISATIONS 表中的行的值。
还接受 cg$ind_type 类型的参数 cg$ind(指示提供的列),还接受一个布尔参数 do_ins(指示应执行插入)。

upd

更新表中的一条记录。
接受 cg$row_type 类型的参数 cg$rec,该参数包含要更新到 ORGANISATIONS 表中的行的值。
还接受 cg$ind_type 类型的参数 cg$ind(指示提供的列),还接受一个布尔参数 do_upd(指示应执行更新)。
更新主键时,还会使用 cg$row_type 类型的参数 cg$pk。

del

删除表中的记录。
接受 cg$pk_type 类型的参数 cg$pk,该参数包含要删除的记录的主键,还接受参数 do_del(指示应执行删除)。

lck

锁定表中的记录。
接受 cg$row_type 类型的参数 cg$old_rec,该参数包含在进行任何更改前要锁定的记录的值。
还接受 cg$ind_type 类型的参数 cg$old_ind(指示在进行任何更改前记录中非空的列),还接受一个用于确定锁定行为的参数 nowait_flag。

slct

使用主键选择记录。
接受 cg$row_type 类型的参数 cg$sel_rec,该参数包含主键,使用此主键提取记录,然后将其返回到记录中。

validate_domain

验证引用代码。
接受 cg$row_type 类型的参数 cg$rec(包含要验证的记录),还接受一个记录 cg$ind(指示要求验证的字段)。此过程提供根据前述的标准 cg_ref_codes 表执行验证的功能。

insert_jn

创建日志行。
接受 cg$row_type 类型的参数 cg$rec,该参数包含来自 DML 语句的值和更改原因。
还接受一个操作参数,记录已发生的 DML 操作的类型。将记录插入标准日志记录表 ORGANISATIONS_JN 中。

此外还包括其他过程,但它们都遵循类似模式。

程序包中的主要过程均将全局变量 called_from_package 设置为 TRUE,指示从表 API 中起动 DML 语句。

优势

使用表 API 对表执行所有操作的结果是将所有业务规则都放在一个单独的位置中 — 从根本上说,就是数据库。如果选择在应用程序的中间层或前端编码业务规则,则在为应用程序添加内容,或允许使用其他工具直接访问表时,可能会轻易错过这些业务规则。

如果授予用户执行程序包的权限,而不是对表执行操作的直接权限,则可确保对表所做的所有更改都通过表 API 进行。这不仅可以确保执行所有验证,而且可以提供一个集中点进行调试。

缺点

此方法相当冗长并会导致大量代码行,这会使缺乏经验的开发人员感到困惑。

从性能角度来讲,这些过程将对单个记录执行 DML。由于这些过程最初是在不能使用 FORALL 子句进行批量处理时开发的,因此它们不能利用该子句提供的性能优势。

维护工作可能会更加困难和耗时。如果更改了表的结构,例如添加了列,则需要对表 API 中的过程进行相应的更改。

触发器

除了表 API 以外,在某些情况下,还会使用 Oracle Designer 为每个表创建一组触发器。在 DML 语句访问表,但未通过表 API 起动 DML 时,应使用触发器。在这种情况下,全局变量 called_from_package 将为空。

触发器在每个操作中的语句级别和行级别创建,这需要发生一些处理。调用的代码将镜像程序包。

触发器根据触发事件和触发级别来命名。使用 ORGANISATIONS 表作为示例,更新触发器的命名方式如下:
cg$BUS_ORGANISATIONS:之前,更新语句级触发器
cg$BUR_ORGANISATIONS:之前,更新行级触发器
cg$AUR_ORGANISATIONS:之后,更新行级触发器
cg$AUS_ORGANISATIONS:之后,更新语句级触发器

优势

使用这些触发器可以确保应用业务规则(如验证),而不必管 DML 语句的来源如何。这可以防止直接更改表的 SQL 绕过验证代码。

缺点

即使是从表 API 程序包起动处理,这些触发器还是会触发。一旦代码确定变量 called_from_package 已设置为 TRUE,就会绕过其余代码。然而,这仍将导致触发器执行,从而带来相关的性能开销。

维护工作可能会更加困难和耗时。如果更改了表的结构(例如,添加了一列),则需要对触发器代码进行相应的更改。

模块 API

此方法的一个扩展是使用 PL/SQL 程序包作为数据源和每个表单块的目标。如果使用此方法,您还将发现每个表单块都有一个程序包,如下所述:

程序包 cgc$BK0020_ORGANISATIONS 用于对 BK0020 表单中的 ORGANISATIONS 块执行操作。

此程序包包含一组过程,它们接受 cgc$rec_tab 类型的 PL/SQL 表。这是一个记录表,这些记录与表单块中包含的字段相匹配。这些过程用于支持表单块的标准插入 (ins)、更新 (upd)、删除 (del)、锁定 (Lck) 和查询 (qry) 行为。每个过程将在 PL/SQL 表的记录周围环回,然后从表 API 中调用相应的过程。

例如,为了更新记录,表单块会将已更改的记录传递给 cgc$BK0020_ORGANISATIONS.upd。对于传递给 cgc$BK0020_ORGANISATIONS.upd 的 PL/SQL 的表中的每个记录,将调用 cg$ORGANISATIONS.upd 过程来更新记录。

此处的例外情况是 qry 过程,它不调用相应的表 API。该过程使用动态 SQL 查询基础表并返回结果。

优势

通常,此方法仅用于复杂的表单,其中由块执行的处理要比在单个表上执行 DML 语句复杂得多。此方法允许将其他代码添加到模块 API,从而极大地增强(或绕过)正常的 Oracle Forms 处理。

缺点

此方法将导致双层 API 结构,这将明显增加应用程序中的代码行。

使用此技术时,维护工作比较困难,因为对表单块所做的更改(如添加字段)需要对两个级别的程序包的每个程序包中的多个过程进行更改。

日志表

Oracle Designer 提供了一种创建日志表的标准方法,该方法将审核父表上的所有 DML,最终记录所有数据更改。通过 Oracle Designer 中的首选项设置,在生成表 DDL 时,将创建一个相应的日志表。日志表的结构将和原始表中的列匹配,并将增加一些用于审核更改操作的列。

通常使用上述的表 API 的 insert_jn 过程填充日志表。

增加的列包括

JN_OPERATION

DML 操作,例如 UPDATE

JN_ORACLE_USER

执行更改的会话的 Oracle 用户名

JN_DATETIME

更改日期和时间

JN_NOTES

更改原因,由表单中的用户提供

JN_APPLN

起动更改的过程的名称,例如 cg$ORGANISATIONS.upd

JN_SESSION

执行更改的会话的会话 ID

优势

此方法提供了有关 DML 语句中的值的全面日志,而且不需要开发人员投入太多的精力,便可轻松回答删除记录或更改记录的人员的问题。

缺点

此方法依赖于 Oracle Designer 表 API 和触发器的使用。如果移除这两项,则不发生日志记录。对于要执行插入日志表的表,许多站点现在都使用人工触发器来替代此方法。

结论

当代码层明显较多,但缺乏经验或不能理解以前的开发团队放置代码的原因时,继承原有系统是一项非常艰难的任务。Oracle Forms 和 Oracle Designer 一直是并且仍是构建大型公司系统的重要工具。因此,尽管许多系统中仍提供最佳实践,但不具备相应背景的初级程序员却很少予以重视。对这些最佳实践的一种理解是将其视为为系统未来的活动保留,而无需探究看似简单的一次性修复程序的代码。


Chris Muir (http://one-size-doesnt-fit-all.blogspot.com) 是 Oracle ACE 总监(Oracle 融合中间件)和高级顾问,还是位于澳大利亚的 SAGE Computing Services 的 Oracle 培训师。他们加起来在 Oracle 开发和数据库技术方面有 40 年的经验,借助在 RDBMS、传统 Oracle 开发工具以及 Oracle Application Express、Oracle JDeveloper 和功能良好的旧式 SQL*Plus 方面的多年经验,他们都显示了丰硕的成果。

Linux下的Job Control

2010年11月05日 06:20 | Comments(2) | Category:Linux | Tags:

 

 

最近开始认真的学习Linux,在看一本基础的Linux教程,其中在书中提到了一些关于Linux中Job控制的知识,由于书籍中讲解的比较凌乱,因此自己整理出来,以防便以后查阅。同时也强制自己开始写一些Blog,让自己慢慢的摆脱拖沓的习惯,开始一些积累。

本文章将介绍下面一些内容:

  • Job的概念
  • Linux下Job Control的一些知识
  • Linux下关于Ctrl-C、Ctrl-Z、Ctrl-D的具体意义
  • 参考资料

 

一.Job的概念

初始Job,记得是在上李彤老师的软件工程的课程中接触的,具体的含义现在早已经不记得了。身边又没有软件工程的书籍可以查阅,因此对于Job的概念自己已经说不清楚了,只能凭借残存在脑海中一些零星片段加上Wiki上面的解释,大概描述了。

Job是指在批处理的环境中,为完成某一任务而进行一系列操作的总称。在个人接触计算机的年代,批处理的环境已经不容见到了,只有一些特殊的行业和环境下还在使用这样的概念,仅在书本中接触过。

在Linux下,可以简单的把Job理解为,一个或多个通过Pipe链接的命令及其运行所完成的任务。

例如,我需要把当前的登录的用户信息,记录到名字为LoginUser的文件中,那么我可能回执行下面命令来完成:

lennon@lennon-laptop:~$ who > LoginUser

那们就可以把从命令的执行到LoginUser文件输出的整个过程成为一个Job。

 

二.Linux下面Job Control的一些知识(包括jobs、bg、fg、kill命令的使用)

1.通过使用‘&’操作符让Job在后台运行

通常我们Linux在终端中,运行某一命令时,终端总是等待某一特定的命令或程序运行完以后,给出一个提示,然后我们才能继续运行下一个命令。如果我们要运行一个比较耗时的命令,但是在该命令运行的同时还需要做另外的一些事,那该怎么办呢?

你可以通过在命令的后面加上一个‘&’操作符,来让一个任务在后台运行。(当然如果你是用GUI界面,你也可以重新打开一个窗口运行新的命令或程序)

例如,我们需要拷贝一个比较大的文件,在拷贝的同时,还要做其他一些事情,那么在拷贝命令后面加上一个‘&’,让它在后台拷贝:

lennon@lennon-laptop:~$ cp /media/bigfile /home/lennon/Downloads/ &
[1] 3526
lennon@lennon-laptop:~$ 

此时,我们可以看到在终端中,执行了某一命令后,给出了一些信息,然后就返回了,接着提示用户可以出入下一个命令了,这样程序或命令就在后台执行了。

在显示的信息中,用方括号括起来的数字,表示系统分配给这个Job的Job Number,这里'[1]'中的1,就是这个Job的Job Number。而后面一个比较大的数字,则是系统分配的进程ID(PID),这个PID在系统就代表这个进程。

在后台运行的job,当其运行完成以后,会在终端中给出一个提示:

lennon@lennon-laptop:~$ rm Downloads/linux_11gR2_database_1of2.zip &
[1] 3666
lennon@lennon-laptop:~$ 
[1]+  Done                    rm Downloads/linux_11gR2_database_1of2.zip

这里,不知到是什么原因,我的机子上,只有当在此输入回车后,这个提示才会显示。

 

2.使用jobs命令,来查看当前系统中的Job

如果我们需要查看当前系统中,有那些job,使用‘jobs’命令:

lennon@lennon-laptop:~$ jobs
[1]-  Running                 cp /media/bigfile /home/lennon/Downloads/ &
[2]+  Stopped                 cat
lennon@lennon-laptop:~$ 

如此时,显示我的系统中有2个job,一个是刚才的正在运行的拷贝命令([1]标识),一个是停止运行的cat命令([2]表示)。这里‘Running’、‘Stopped’表示任务的状态。

jobs命令参考

命令名称:jobs
使用权限:所有权限
命令描述:列出系统中的job。注意:不是所有的shell都能使用次命令
语法:jobs [-p | -l] [-n] [-p] [-x] [job id]
参数:
-p | -l : Report the process group ID and working directory of the jobs.
-n      : Display only jobs that have stopped or exited since last notified.
-p      : Displays only the process IDs for the process group leaders of the selected jobs.
-x      : Replace any job_id found in command or arguments with the corresponding 
           process group ID, and then execute command passing it arguments.
job id  : The job id.

 

 

3.Suspend key 和 bg命令的使用(将一个正在运行的job放到后台运行)

如果你在运行job前,并不知道该job的运行情况,但在job运行了以后,发现这个是耗时的任务,并想将其放入到后台运行,这样你可以在这个任务运行的同时,完成一些其他的事情,那么你可以这样做:

使用挂起键(Suspend Key,通常是Ctrl-Z)将该任务挂起(也就是暂停),然后使用‘bg’命令在后台让该job恢复执行。

lennon@lennon-laptop:~$ cp bigfile bigfile.bac
^Z
[1]+  Stopped                 cp bigfile bigfile.bac
lennon@lennon-laptop:~$ bg %1
[1]+ cp bigfile bigfile.bac &
lennon@lennon-laptop:~$

使用Ctrl-Z后,系统会将当前正在运行的job暂停,将其移至后台,给出用户改任务的提示(包括job number、状态、job),然后提示用户输入下一个命令。

在job挂起后,可以使用‘bg’命令,让job恢复到刚才中断的地方继续运行并将其放到后台运行。使用‘bg %job number’来指定你需要对哪一个job进行操作,这里‘%’告诉系统后面的数字是一个job number(不要‘%’可能也可以)。当然在系统中只有一个job的时候,你也可以忽略改参数。

bg命令参考:

命令名称:bg
使用权限:所有权限
命令描述:在后台恢复已停止的job继续运行。注意该命令不能在所有的Unix的shell下运行
语法:bg [-l] [-p] [-x] [job]
参数:
-l    : Report the process group ID and working directory of the jobs.
-p    : Report only the process group ID of the jobs.
-x    : Replace any job_id found in command or arguments with the corresponding process  
         group ID, and then execute command passing it arguments.
job   : Specifies the job that you want to run in the background.

 

4.使用fg命令,将在后台的job换到前台

当你需要将在后台的job换到前台时,使用‘fg %job number’命令(也许不要‘%’也可以)。

lennon@lennon-laptop:~$ cp bigfile bigfile.bac &
[1] 3815
lennon@lennon-laptop:~$ fg 1
cp bigfile bigfile.bac

fg命令参考:

命令名称:fg
使用权限:所有权限
命令描述:将后台的任务移至前台,如果是该任务处于暂停状态,则恢复该任务的运行。
         注意该命令不是在所有的shell中都能运行。
语法:fg [%job]
参数:
%job : Specifies the job that you want to run in the foreground.

 

5.如何结束一个job

如果你想结束一个正在运行的job,可以使用中断键(interrupt key,通常是Ctrl-C)来结束。

lennon@lennon-laptop:~$ cp bigfile bigfile.bac 
^C
lennon@lennon-laptop:~$ 

如果上面方法无法正常工作,那么你可以考虑使用Ctrl-Z(Suspend key)来暂停job,使用'jobs'命令来查看这个job的job number,然后通过'kill'命令来结束这个job.

lennon@lennon-laptop:~$ cp bigfile bigfile.bac 
^Z
[1]+  Stopped                 cp bigfile bigfile.bac
lennon@lennon-laptop:~$ jobs
[1]+  Stopped                 cp bigfile bigfile.bac
lennon@lennon-laptop:~$ kill %1
lennon@lennon-laptop:~$ 
[1]+  Terminated              cp bigfile bigfile.bac

'kill %1'中的'%'告诉系统,后面的数字是一个job number。默认情况下,kill将会向程序发送一个termination signal(-TERM)。如果这个信号不起作用,考虑使用'kill -kill %job number'来发送一个kill signal(-KILL)。

'kill'命令的使用,就看man把,info也可以,这里不说了,上面东西太多。不过常用的也就'kill %job number'、'kill -kill %job number'、'kill [-kill] PID'。

 

三.Linux下Ctrl-Z、Ctrl-C、Ctrl-D的具体含义

初识Linux,可能会对Ctrl-Z、Ctrl-C、Ctrl-D的具体含义混淆不清,因为这三个按键都能够在一些情况下使shell退出正在执行的命令或程序,提示用户输入下一个命令,从而对初识者造成假象,认为三个键的功能一样,但具体情况下哪个按键能起作用、起什么作用却拿捏不定。

  • Ctrl-Z:该键是linux下面默认的挂起键(Suspend Key),当键入Ctrl-Z时,系统会将正在运行的程序挂起,然后放到后台,同时给出用户相关的job信息。此时,程序并没有真正的停止,用户可以通过使用fg、bg命令将job恢复到暂停前的上下文环境,并继续执行。
  • Ctrl-C:该键是linux下面默认的中断键(Interrupt Key),当键入Ctrl-C时,系统会发送一个中断信号给正在运行的程序和shell。具体的响应结果会根据程序的不同而不同。一些程序在收到这个信号后,会立即结束并推出程序,一些程序可能会忽略这个中断信号,还有一些程序在接受到这个信号后,会采取一些其他的动作(Action)。当shell接受到这个中断信号的时候,它会返回到提示界面,并等待下一个命令。
  • Ctrl-D:该键是Linux下面标准输入输出的EOF。在使用标准输入输出的设备中,遇到该符号,会认为读到了文件的末尾,因此结束输入或输出。

 

四.参考文献

uuencode工具的使用

2010年11月05日 04:50 | Comments(0) | Category:Linux | Tags:

今天在看一本linux的书籍,其中一个章节的练习中提到了uuencode这个工具,初始不懂,查看man帮助之后,依然不知到如何使用,后面google之,终于明白了,记录之,以防日后忘记,又做重复工作。

下面之介绍简单的uuencode工具的使用。

其实uuencode与uudecode是一套工具,都包含在GNU sharutils package中。其中uuencode用于将二进制文件编码成为ACSII,而uudecode用于将使用uuencode编码的文件还原为原来的二进制文件。

在man中,这样描述uuencode、uudecode的使用语法:

uuencode [-m] [ file ] name
uudecode [-o outfile] [ file ]...

在uuencode中,file指明需要编码的文件,如果不指明,则系统以标准输入设备输入,而name用于指明在uuencode编码后的输出数据,使用uudecode还原后,还原的文件名称。这里需要指出的是,uuencode的输出都是直接输出到标准的输出设备,例如:

lennon@lennon-laptop:~/Documents/LearnLinux$ uuencode colors.2.gz test_uuencode
begin 644 test_uuencode
M'XL("`P;Q$P``V-O;&]R<RXR`"M*3>%*RBE-Y4HO2DW-XZI,S<G)+^?*+TK,
-2T_E`@!NZQGI'0```````
end

uuencode输出数据,都是以beging开头,以end结尾的。第一行中,记录编码的信息

  • begin后面的644是unix档案调用的权限代码
  • 而test_uuencode,指明使用uudecode还原该数据后的文件名
  • 后面的字符,就是编码后的结果

如果执行下面命令:

lennon@lennon-laptop:~/Documents/LearnLinux$ uuencode colors.2.gz test_uuencode > uuencode_result

那么将会把编码后的信息存入uuencode_result文件中(由于uuencode的输出是标准输出,所以我们使用重定向命令,将其重定向到uuencode_result中),接着使用解码工具解码:

lennon@lennon-laptop:~/Documents/LearnLinux$ uudecode uuencode_result

将会把uuencode_result文件解码,并且将解码后的结果存放到test_uuencode文件中。

好了,基本的使用就介绍到这里。man中不容易理解的地方就是name参数的意义,其实实在使用uudecode的时候,这个参数的作用才能知道。uuencode、uudecode是成对使用的。理解这个后,其他详细的信息,就参阅man帮助文档吧

参考资料:

 

来源:http://www.isstudy.com/linux/731.html

命令名称:uuencode

使用权限:所有使用者

使用方式:

uuencode [-hv] [sourcefile] targetfile

说明:
早期在许多 unix 系统的传送协定只能传送七位元字元,并不支援二进位档案,像中文文字档就有用到八位元,所以无法完整地送到另一架机器上。 uuencode 指令,可以将二进位档转换成七位元的档案,传送到另一架机器上再以 uudecode 还原。最常见的是用在以电子邮件传送二进位档。uuencode 编码后的资料都以 begin 开始,以 end 作为结束。
begin 644 decode.dat  
(内容编码)  
`  
end
begin 后的 644 是 unix 档案的调用权限代码。而 decode.dat 则是还原后的档名。
参数:  
h 列出指令使用格式 (help)  
v 列出版本讯息 (version)
范例:
将 source.dat 编码后存成 file.uud 档。由于 uuencode 会将编码后的资料在荧幕上列出,所以必须将资料导入 file.uud 中,而 target.dat 是 file.uud 还原后的档案名字,而不是编码后的档案。

uuencode [-hv] [sourcefile] targetfile

执行指令后,由标准输入装置读入资料,简单地说是由键盘输入后在荧幕显示的资料,按 Crtl d 键后就会被编码,输出至 file.uud 中。由于程序是以行为处理单位,若无法跳出程序,请多按几次 Ctrl d 直到程序结束为止。 安全技术

uuencode target.dat > file.uud

类似上一个指令,不过使用一个导入的技巧,将 source.dat 压缩后的资料传送给 uuencode 处理之后,存成 file.uud 档。 file.uud 档还原后就是 source.dat.Z 档。

compress -c source.dat | uuencode source.dat.Z > file.uud

显示版本讯息后,结束执行程序。

uuencode -v