hand标准化

DO070 Hand_PLSQL_框架_应用指南



本文档详细描述了在   客户化项目开发中SQL/PLSQL的一些规范和开发指南,用于指导开发员遵守开发标准、使用开发模板、快速进入开发角色。

本文内容包括:

1、  对业务表维护API模板

2、  并发程序的模板

3、  如何调试代码

 

使用框架和不使用框架相比,在刚开始使用的时候,会觉得更麻烦。但是习惯了也就不麻烦了。

问题是:我们为什么要使用框架?要深刻的理解这个问题,我们先看两个故事。这个故事在当前目录的Story 目录下。看完这两个故事,我们可以理解了,如果Oracle  的代码没有Debug功能。我们如何去分析解决问题? 那将是不可设想的。

我们公司的客户越来越多,客户化开发也越来越多。随之而来的是可能产生的问题也越来越多。我们的Hotline 所接到的问题中,有很大一部分是客户化开发的问题。而解决这些问题的人,一般都不是当初开发的人。而是在hotline 工作的人。可想而知,我们的代码没有Debug 功能,没有Debug的习惯。以后出了问题,让他们怎么查?

基于以上原因,我们建议我们的代码都要使用我们的开发框架,因为这个框架是一个具备Debug功能的框架。和Oracle  的Debug框架是一致的。写出来的代码风格跟Oracle是一致的。

 

 

三个Package介绍

1、  HAND_DEBUG.pck

2、  HAND_API.pck

3、  HAND_CONC_UTL.pck

这三个package都不大,熟读以后有助于我们使用其中的Api.

 

 

业务表维护模板

对于业务表,必须提供公开函数来维护,禁止外部程序直接操作表,一般规则如下图所示:

1、Table API 实现表基本操作:Insert、Lock、Update、Delete。

2、Private API调用TableAPI来操作表,该层实现验证表的必输字段,同时通过调用其它API提供的Public API来实现维护表的业务逻辑。

3、Public API是公开的API,供外部程序调用,该层只做对传入参数作简单验证;然后调用Private API。

4、对于所有的验证,提供对立的Valiate API

5、对于简单的业务表,如基础数据,该数据只在Form中维护,可以只建立TableAPI。

6、各程序包的命名规则:

Table API:表名 + ‘_PKG’    (table api)

Private API:表名 + ‘_PVT’  (private api)

Public API:表名 + ‘_PUB’    (public api)

Valiate API:表名 + ‘_UTL’   (validate api)

 

对于多个API间相互调用图例:

 

以下介绍各API的模板和使用方法

Table API

该程序包一般包括以下几个过程:insert_row、lock_row、update_row、delete_row

包头定义:

PROCEDURE insert_row(

              x_row_id in out varchar2,

              x_primary_key in out number,

              p_field1 in …

              … );

PROCEDURE lock_row(

              p_primary_key in number,

              p_field1 in …

              … );

PROCEDURE update_row(

              p_primary_key in number,

              p_field1 in …

              … );

PROCEDURE delete_row(

              p_primary_key in number);

 

对于表中含有object_version_number,其过程lock_row可简化为

PROCEDURE lock_row(

              p_primary_key in number,

              p_object_version_number in number );

 

 

包体如下:

PROCEDURE insert_row(

              x_row_id in out varchar2,

              x_primary_key in out number,

              p_field1 in …

              … )

IS

  CURSOR C

  IS SELECT ROWID FROM <TABLE>

       WHERE <primary_key> = p_primary_key;

BEGIN

  INSERT INTO <TABLE> (

       field1, field2, … )

  VALUES (

       p_field1, p_field2, … );

 

OPEN c;
    FETCH c INTO x_row_id;
    IF (c%NOTFOUND) THEN
      CLOSE c;
      RAISE NO_DATA_FOUND;
    END IF;
    CLOSE c;

END insert_row;

 

PROCEDURE lock_row(

              p_primary_key in number,

              p_field1 in …

              … )

IS

    CURSOR c1

    IS SELECT field1,field2,…

         from <TABLE>

        where <primary_key> = p_primary_key

        FOR UPDATE OF <primary_key> NOWAIT;

    tlinfo c1%ROWTYPE;

  BEGIN

    OPEN c1;

    FETCH c1 INTO tlinfo;

    IF (c1%NOTFOUND) THEN

      CLOSE c1;

      fnd_message.set_name('FND', 'FORM_RECORD_DELETED');

      app_exception.raise_exception;

    END IF;

    CLOSE c1;

 

IF (tlinfo.field1 = p_field1 or (tlinfo.field1 is null and p_field1 is null))

  AND (tlinfo.field2 = p_field2 or (tlinfo.field2 is null and p_field2 is null))

   …

THEN

      NULL;

    ELSE

      fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');

      app_exception.raise_exception;

END IF;

END lock_row;

 

PROCEDURE update_row(

              p_primary_key in number,

              p_field1 in …

              … )

IS

BEGIN

    UPDATE <TABLE> SET

                field1                = p_field1,

                field1                = p_field1,

                …

    WHERE <primary_key> = p_primary_key;

 

    IF (SQL%NOTFOUND) THEN

      RAISE NO_DATA_FOUND;

    END IF;

END update_row;

 

PROCEDURE delete_row(

              p_primary_key in number)

IS

BEGIN

    DELETE FROM <TABLE>

    WHERE  <primary_key> = p_primary_key;

 

    IF (SQL%NOTFOUND) THEN

      RAISE NO_DATA_FOUND;

    END IF;

END delete_row;

对于表中含有object_version_number,其过程lock_row可简化为

PROCEDURE lock_row(

              p_primary_key in number,

              p_object_version_number in number )

    CURSOR c1

    IS SELECT object_version_number

         from <TABLE>

        where <primary_key> = p_primary_key

        FOR UPDATE OF <primary_key> NOWAIT;

    tlinfo c1%ROWTYPE;

  BEGIN

    OPEN c1;

    FETCH c1 INTO tlinfo;

    IF (c1%NOTFOUND) THEN

      CLOSE c1;

      fnd_message.set_name('FND', 'FORM_RECORD_DELETED');

      app_exception.raise_exception;

    END IF;

    CLOSE c1;

 

IF (tlinfo.object_version_number = p_object_version_number)

THEN

      NULL;

    ELSE

      fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');

      app_exception.raise_exception;

END IF;

END lock_row;

 

 

 

 

Private API / Public API 的过程模板

为统一风格、简化程序、提供统一的例外处理和调试功能,该程序包中的API使用以下模板

  PROCEDURE create_temp
        ( p_api_version           IN  NUMBER,
          p_init_msg_list         IN  VARCHAR2 DEFAULT fnd_api.g_false,
          p_commit                IN  VARCHAR2 DEFAULT fnd_api.g_false,
          x_return_status         OUT NOCOPY VARCHAR2,
          x_msg_count             OUT NOCOPY NUMBER,
          x_msg_data              OUT NOCOPY VARCHAR2,
          p_field1                IN  NUMBER,
          x_temp_id               OUT NOCOPY NUMBER )
  IS
    l_api_name        CONSTANT VARCHAR2(30) := 'CREATE_TEMP';
    l_api_version CONSTANT NUMBER    := 1.0;

  BEGIN
    -- start activity to create savepoint, check compatibility
    -- and initialize message list, include debug message hint to enter api

    x_return_status := HAND_API.start_activity(
                           p_pkg_name      => g_pkg_name,
                           p_api_name      => l_api_name,
                           p_api_type      => g_api_type,
                           p_init_msg_list => p_init_msg_list,
                           l_api_version   => l_api_version,
                           p_api_version   => p_api_version);
    -- check if activity started successfully
    IF (x_return_status = fnd_api.g_ret_sts_unexp_error) THEN
       RAISE
fnd_api.g_exc_unexpected_error;
    ELSIF (x_return_status = fnd_api.g_ret_sts_error) THEN
       RAISE
fnd_api.g_exc_error;
    END IF;
   
  -- API body
    -- logging parameters
    /*
    IF l_debug = 'Y' THEN
      hand_debug.log('p_xxx : ' || p_xxx,2);
    END IF;
    */

   
    -- Prev create logic (call other public api)
   
    -- Insert the row into the table by calling the table handler.
    /*
    BEGIN
      -- set primary key from sequence
      select temp_s.nextval into l_temp_id from dual;
      Hand_plsql_template_pkg.insert_row(
                     p_temp_id                     => ,
                     p_field1                      => ,
                     p_field2                      => ,
                     p_object_version_number       => ,
                     p_creation_date               => ,
                     p_created_by                  => ,
                     p_last_udpate_date            => ,
                     p_last_updated_by             => ,
                     p_last_udpate_login           =>  );
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
       x_return_status := fnd_api.g_ret_sts_unexp_error;
       HAND_API.set_message( p_app_name => 'CUX',
                            p_msg_name => 'XH_TABLE_HANDLER_ERROR' );
       RAISE fnd_api.g_exc_unexpected_error;
    END;
    */

    -- Post create logic (call other public api)
  -- API end body

    -- end activity, include debug message hint to exit api

    HAND_API.end_activity( p_pkg_name        => g_pkg_name,
                      p_api_name        => l_api_name,
                          p_commit          => p_commit,
                          x_msg_count      => x_msg_count,
             x_msg_data       => x_msg_data );
  EXCEPTION
    WHEN
fnd_api.g_exc_error THEN
      x_return_status := HAND_API.handle_exceptions(
                          p_pkg_name  => g_pkg_name,
                          p_api_name  => l_api_name,
                          p_api_type  => g_api_type,
                          p_exc_name  => HAND_API.g_exc_name_error,
                          x_msg_count => x_msg_count,
                          x_msg_data  => x_msg_data);
    WHEN fnd_api.g_exc_unexpected_error THEN
      x_return_status := HAND_API.handle_exceptions(
                          p_pkg_name  => g_pkg_name,
                          p_api_name  => l_api_name,
                          p_api_type  => g_api_type,
                          p_exc_name  => HAND_API.g_exc_name_unexp,
                          x_msg_count => x_msg_count,
                          x_msg_data  => x_msg_data);
    WHEN OTHERS THEN
      x_return_status := HAND_API.handle_exceptions(
                          p_pkg_name  => g_pkg_name,
                          p_api_name  => l_api_name,
                          p_api_type  => g_api_type,
                          p_exc_name  => HAND_API.g_exc_name_others,
                          x_msg_count => x_msg_count,
                          x_msg_data  => x_msg_data);
  END create_temp;

1、过程必要的参数

参数名

类型

数据类型

描述

 

 

 

 

P_API_VERSION

IN

NUMBER

API版本号,用于检查API版本

P_INIT_MSG_LIST

IN

VARCHAR2

是否初始化消息队列

P_COMMIT

IN

VARCHAR2

是否自动提交更改

X_RETURN_STATUS

OUT

VARCHAR2

返回API执行状态

S      成功

E     错误

U     例外

X_MSG_COUNT

OUT

VARCHAR2

返回错误消息的个数

X_MSG_DATA

OUT

VARCHAR2

返回第一个错误信息的内容

其中:

P_COMMIT:对于无数据更新操作可以不要,对于这类的API,需要将g_api_type设置为NULL,且调用HAND_API.end_activity不要传递p_commit参数。

 

验证API

所有的验证API只返回验证的结果状态,验证的错误信息直接加入消息队列。

当验证不通过时,使用HAND_API.set_message将验证错误信息加入到消息队列。

验证API可以不处理例外,而由调用者处理。

以下提供两种处理方式,一个不带例外处理,一个带例外处理。

不带例外处理:

  PROCEDURE validate_currency_code(

                   p_currency_code            IN VARCHAR2,

                   x_return_status            OUT NOCOPY VARCHAR2)

  IS

    CURSOR c_currencies

    IS SELECT 1

         FROM fnd_currencies

        WHERE currency_code = p_currency_code;

   

    l_dummy          VARCHAR2(1);

  BEGIN

    x_return_status := fnd_api.g_ret_sts_success;

   

    OPEN c_currencies;

    FETCH c_currencies INTO l_dummy;

    IF c_currencies%NOTFOUND THEN

      x_return_status := fnd_api.g_ret_sts_error;

      HAND_API.set_message( p_app_name       => 'CUX',

                           p_msg_name       => 'XH_INVALID_CURRENCY_CODE',

                           p_token1         => 'P_CURRENCY_CODE',

                           p_token1_value   => p_currency_code);

    END IF;

    CLOSE c_currencies;

  END validate_currency_code;

带例外处理:

  PROCEDURE validate_currency_code1(

                   p_currency_code            IN VARCHAR2,

                   x_return_status            OUT NOCOPY VARCHAR2)

  IS

    CURSOR c_currencies

    IS SELECT 1

         FROM fnd_currencies

        WHERE currency_code = p_currency_code;

   

    l_api_name           CONSTANT VARCHAR2(30) := 'VALIDATE_CURRENCY_CODE1';

    l_dummy         VARCHAR2(1);

  BEGIN

    x_return_status := fnd_api.g_ret_sts_success;

   

    OPEN c_currencies;

    FETCH c_currencies INTO l_dummy;

    IF c_currencies%NOTFOUND THEN

      CLOSE c_currencies;

      HAND_API.set_message( p_app_name       => 'CUX',

                           p_msg_name       => 'XH_INVALID_CURRENCY_CODE',

                           p_token1         => 'P_CURRENCY_CODE',

                           p_token1_value   => p_currency_code);

      RAISE fnd_api.g_exc_error;

    END IF;

    CLOSE c_currencies;

  EXCEPTION

    WHEN fnd_api.g_exc_error THEN

       x_return_status := fnd_api.g_ret_sts_error;

    WHEN fnd_api.g_exc_unexp_error THEN

       x_return_status := fnd_api.g_ret_sts_unexp_error;

    WHEN OTHERS THEN

       IF fnd_msg_pub.check_msg_level(fnd_msg_pub.g_msg_lvl_unexp_error) THEN

            fnd_msg_pub.add_exc_msg( g_pkg_name, l_api_name );

       END IF;

       x_return_status := fnd_api.g_ret_sts_unexp_error;

  END validate_currency_code1;

 

 

 

并发程序模板

并发程序的过程第1,2个参数是固定的:

参数名

类型

数据类型

描述

 

 

 

 

ERRBUF

OUT

VARCHAR2

并发程序返回错误信息

RETCODE

OUT

VARCHAR2

并发程序返回状态

0  成功

1  警告

2  错误

 

 

CREATE OR REPLACE PACKAGE BODY XH_CONC_TEMPLATE_PUB AS

/*===============================================

  Copyright (C)  Xieheng Co.,Ltd.                        

              AllRights Reserved                                           

================================================

* ===============================================

*   PROGRAM NAME:

*               

*   DESCRIPTION:

*               

*   HISTORY:

*     1.00   2005-09-26   xxxxxx   Creation

*                    

* ==============================================*/

 

  G_PKG_NAME CONSTANT VARCHAR2(30):= 'XH_CONC_TEMPLATE_PUB';

 

  PROCEDURE main(  errbuf            OUT VARCHAR2,

                   retcode           OUT VARCHAR2,

                   p_parameter1      IN  VARCHAR2,

                   p_parameter2      IN  VARCHAR2)

  IS

    l_return_status    VARCHAR2(30);

    l_msg_count        NUMBER;

    l_msg_data         VARCHAR2(2000);

  BEGIN

    retcode := '0';

    -- concurrent header log

    Hand_conc_utl.log_header;

   

    -- convert parameter data type, such as varchar2 to date

    -- l_date := fnd_conc_date.string_to_date(p_parameter1);

 

  -- conc body 

    -- call process api

    process_request(

                p_init_msg_list   => fnd_api.g_true,

                p_commit          => fnd_api.g_false,

                x_return_status   => l_return_status,

                x_msg_count       => l_msg_count,

                x_msg_data        => l_msg_data,

                p_xxx             => p_parameter1

                p_xxx             => p_parameter2);

    IF l_return_status = fnd_api.g_ret_sts_error THEN

      RAISE fnd_api.g_exc_error;

    ELSIF l_return_status = fnd_api.g_ret_sts_unexp_error THEN

      RAISE fnd_api.g_exc_unexpected_error;

    END IF;

  -- conc end body

 

    -- concurrent footer log

    Hand_conc_utl.log_footer;

   

  EXCEPTION

    WHEN fnd_api.g_exc_error THEN

      Hand_conc_utl.log_message_list;

      retcode := '1';

      errbuf := l_msg_data;

    WHEN fnd_api.g_exc_unexpected_error THEN

      Hand_conc_utl.log_message_list;

      retcode := '2';

      errbuf := l_msg_data;

    WHEN OTHERS THEN

      fnd_msg_pub.add_exc_msg(p_pkg_name => g_pkg_name, p_procedure_name => 'MAIN' );

      Hand_conc_utl.log_message_list;

      retcode := '2';

      errbuf := SQLERRM;

  END main;

 

END XH_CONC_TEMPLATE_PUB;

 

代码调试

使用模板开发的程序包默认具有调试的功能。

开发员可以在任何代码段内调用HAND_DEBUG.LOG进行调试信息的输出。

该调试包可以根据预置文件的设置情况,将信息输出到fnd_log_messages 表、服务器文件、并发程序log文件。

启动调试使用系统标准的日志预置文件进行控制

预置文件名

描述

 

 

FND:启用调试日志

是否启动调试

FND:调试日志级别

调试须设置为过程 或 对帐单

FND:调试日志模块

调试客户化程序,设置为%

 

启动后,日志默认输出到表fnd_log_message中,若需要产生日志文件需要设置一下两个预置文件

预置文件名

描述

 

 

CUX: Debug Log mode

FILE               输出到日志文件,但并发输出到并发log文件

FILEONLY   强制输出到日志文件

TABLE           输出到表fnd_log_messages

CUX: Debug Log File

设置服务器文件名,如/usr/tmp/hand.log,则产生文件为hand_<USER>.log文件,<USER>为登录用户名。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值