CREATE FUNCTION 语句

CREATE FUNCTION 和 CREATE PROCEDURE 语句定义可调用的函数或过程(通常称为例程)。

语法

注:
  1. 如果例程类型为 FUNCTION,则方向指示符(IN、OUT、INOUT)对于每个参数都是可选的。不过,出于记录目的,为所有任意类型的新例程指定方向指示符是很好的编程做法。
  2. 使用 NAMESPACE 或 NAME 子句时,它们的值是 CHARACTER 类型的隐式常量。有关使用 CONSTANT 变量的信息,请参阅 DECLARE 语句
  3. 如果例程类型为 FUNCTION,则无法指定 DATABASE 的 LANGUAGE。

概述

CREATE FUNCTION 和 CREATE PROCEDURE 语句定义可调用的函数或过程(通常称为例程)。

注: 在先前版本的本产品中,CREATE FUNCTION 和 CREATE PROCEDURE 具有不同的用途和功能。但一直以来,它们的变化都不大。函数与过程的唯一不同之处列在了语法图下的注释 1 和 3 中。

例程在创建可复用的代码块时很有用,这些代码块可以独立执行许多次。它们可以实现为一系列 ESQL 语句、一个 Java 方法或数据库存储过程。这种灵活性意味着语法图中的某些子句并非适用于(或允许用于)所有类型的例程。

每个例程都有一个名称,该名称在例程所在的模式中必须是唯一的。这意味着不能重载例程名称;如果代理检测到重载的例程,会抛出异常。

LANGUAGE 子句指定用于编写例程主体的语言。选项为:
DATABASE
作为数据库存储过程调用的过程。
ESQL
作为 ESQL 例程调用的过程。
JAVA
Java 类中作为静态方法调用的过程。
Unspecified
如果未指定 LANGUAGE 子句,除非您指定了 EXTERNAL NAME 子句,这种情况下缺省语言为 DATABASE,否则缺省语言为 ESQL。
以下是使用 LANGUAGE 子句时的局限性。请勿:
  • 将 ESQL 选项与 EXTERNAL NAME 子句一起使用
  • 使用 DATABASE 或 JAVA 选项而不使用 EXTERNAL NAME 子句
  • 将 DATABASE 选项与例程类型 FUNCTION 一起使用

使用 RoutineName 子句可以指定例程的名称,使用 ParameterList 子句可以指定例程的参数。如果 LANGUAGE 子句指定 ESQL,则必须使用一条 ESQL 语句实现该例程。此语句如果是复合语句(BEGIN ... END),就能发挥最大的效用,因为它可以包含所有必要的 ESQL 语句完成其功能。

或者,您也可以指定 LANGUAGE 子句而不是 ESQL,这样就不必为例程提供 ESQL 主体。LANGUAGE 子句允许您使用 EXTERNAL NAME 子句引用实际的例程主体,不管它在代理外部的什么地方。有关使用 EXTERNAL NAME 子句的更多详细信息,请参阅调用存储过程调用 Java 例程

任何 LANGUAGE 类型的例程都可以具有 IN、OUT 和 INOUT 参数。调用者可以使用它们将多个值传递到例程中,并收回多个更新的值。这是除 RETURNS 子句外,例程还可以具有的参数。RETURNS 子句允许例程将值传递回调用者。

用不同语言实现的例程对可以传递或返回的数据类型有它们自己的限制,下面描述了这些限制。返回值的数据类型必须与定义用来从例程返回的值的数据类型相匹配。另外,如果将例程定义为具有返回值,例程的调用者就不能忽略它。关于更多详细信息,请参阅 CALL 语句

例程可以在模块或模式中定义。在模块中定义的例程在当前节点的作用域是本地,这意味着只有属于同一模块(或节点)的代码才能调用它们。但是,在模式作用域中定义的例程可以被任何以下代码调用:
  • 同一模式中的代码。
  • 任何其他模式中的代码(只要满足以下任一条件即可):
    1. 其他模式的 PATH 子句包含被调用例程的路径,或者
    2. 调用的例程是使用标准名称调用的,标准名称即名称加上其模式名称作为前缀,这两者之间用句点隔开。
因此,如果需要在多个节点中调用同一个例程,则在模式中定义它。

对于任何语言或例程类型,例程的调用方法必须与例程的声明方式相匹配。如果例程具有 RETURNS 子句,则使用 FUNCTION 调用语法或带有 INTO 子句的 CALL 语句。反之,如果例程没有 RETURNS 子句,则必须使用不带 INTO 子句的 CALL 语句。

参数方向

传递给例程的参数始终具有与其关联的方向。它可以是下列任何一个方向:
IN
例程无法更改参数的值。允许参数使用 NULL 值,并且可以将该值传递给例程。
OUT
当调用的例程收到 OUT 时,传递给例程的参数始终具有正确数据类型的 NULL 值。无论例程被调用前该参数的值是什么,传递给例程之后都是 NULL。例程可以更改参数的值。
INOUT
INOUT 既是 IN 参数也是 OUT 参数。它将值传递给例程,并且例程可以更改传进来的值。允许参数使用 NULL 值,并可以将该值传进传出例程。

如果例程类型为 FUNCTION,则方向指示符(IN、OUT、INOUT)对于每个参数都是可选的。不过,出于记录目的,为所有任意类型的新例程指定方向指示符是很好的编程做法。

声明为 CONSTANT 的 ESQL 变量(或对声明为 CONSTANT 的变量的引用)不能具有 OUT 或 INOUT 方向。

ESQL 例程

ESQL 例程使用 ESQL 编写,并且其 LANGUAGE 子句为 ESQL。ESQL 例程的主体通常是 BEGIN … END 格式的复合语句,它包含多个语句用来处理传递给例程的参数。

ESQL 示例 1

以下示例显示了与数据库例程示例 1中相同的过程,但它作为 ESQL 例程而不作为存储过程实现。CALL 语法和该例程的结果与Java 例程的局限性中的相同。
CREATE PROCEDURE swapParms (
    IN parm1 CHARACTER,
    OUT parm2  CHARACTER,
    INOUT parm3 CHARACTER )
             BEGIN
      SET parm2 = parm3;
      SET parm3 = parm1;
 END;

ESQL 示例 2

此过程示例显示如何递归使用 ESQL例程。它通过访问指定的起始点处和起始点下面的所有位置来解析树,并报告发现的内容:

SET OutputRoot.MQMD = InputRoot.MQMD;

    DECLARE answer CHARACTER;
    SET     answer = '';

    CALL navigate(InputRoot.XML, answer);
    SET OutputRoot.XML.Data.FieldNames = answer;


    CREATE PROCEDURE navigate (IN root REFERENCE, INOUT answer CHARACTER)
               BEGIN
    SET answer = answer || 'Reached Field... Type:'
    || CAST(FIELDTYPE(root) AS CHAR)||
        ': Name:' || FIELDNAME(root) || ': Value :' || root || ': ';

        DECLARE cursor REFERENCE TO root;
        MOVE cursor FIRSTCHILD;
        IF LASTMOVE(cursor) THEN
            SET answer = answer || 'Field has children... drilling down ';
             ELSE
            SET answer = answer || 'Listing siblings... ';
    END IF;

        WHILE LASTMOVE(cursor) DO
            CALL navigate(cursor, answer);
            MOVE cursor NEXTSIBLING;
    END WHILE;

        SET answer = answer || 'Finished siblings... Popping up ';
    END;

当给出以下输入消息时:

<Person>
  <Name>John Smith</Name>
  <Salary period='monthly' taxable='yes'>-1200</Salary>
</Person>

该过程产生以下输出(它们已经过手动格式化):

  Reached Field... Type:16777232: Name:XML: Value :: Field has children... drilling down 
  Reached Field... Type:16777216: Name:Person: Value :: Field has children... drilling down 
  Reached Field... Type:16777216: Name:Name: Value :John Smith: Field has children... drilling down 
  Reached Field... Type:33554432: Name:: Value :John Smith: Listing siblings... Finished siblings... Popping up
Finished siblings... Popping up
    Reached Field... Type:16777216: Name:Salary:Value :-1200: Field has children... drilling down 
  Reached Field... Type:50331648: Name:period: Value :monthly: Listing siblings... Finished siblings... Popping up
  Reached Field... Type:50331648: Name:taxable: Value :yes: Listing siblings... Finished siblings... Popping up 
  Reached Field... Type:33554432: Name:: Value :-1200: Listing siblings... Finished siblings... Popping up 
Finished siblings... Popping up
  Finished siblings... Popping up
  Finished siblings... Popping up

Java 例程

Java 例程作为 Java 方法实现,并且其 LANGUAGE 子句为 JAVA。对于 Java 例程,ExternalRoutineName 必须包含要调用的 Java 方法的类名和方法名。请按如下格式指定 ExternalRoutineName
 >>--"-- className---.---methodName--"--------------><
其中 className 标识包含该方法的类,methodName 标识要调用的方法。如果类是程序包的一部分,类标识部分必须包含完整的程序包前缀;例如“com.ibm.broker.test.MyClass.myMethod”。

为找到 Java 类,代理会按部署 Java 类中的描述进行搜索。

您要调用的任何 Java 方法都必须具有以下基本特征符:
public static <return-type> <method-name> (< 0 - N parameters>)

其中 <return-type> 必须是 ESQL 到 Java 数据类型映射内表中的 Java IN 数据类型列表(REFERENCE 类型除外,它不能作为返回值),或者 Java 空数据类型。参数的数据类型也必须在 ESQL 到 Java 数据类型映射表中。另外,Java 方法的特征符中不能有 exception throws 子句。

Java 方法的特征符必须与 ESQL 例程的方法声明匹配。此外,您还必须遵守以下规则:
  • 确保 Java 方法名(包括类名和所有包限定符)与过程的 EXTERNAL NAME 匹配。
  • 如果 Java 的返回类型为空,请勿在 ESQL 例程的定义中放置 RETURNS 子句。反之,如果 Java 的返回类型不为空,则必须在 ESQL 例程的定义中放置 RETURNS 子句。
  • 确保每个参数的类型和方向按照 ESQL 到 Java 数据类型映射内表中列出的规则与 ESQL 声明匹配。
  • 确保方法的返回类型与 RETURNS 子句的数据类型匹配。
  • 将 EXTERNAL NAME 括在引号中,因为它必须至少包含“class.method”。
  • 如果要调用重载的 Java 方法,必须为每个重载的方法创建一个单独的 ESQL 定义,并赋给每个 ESQL 定义一个唯一的例程名称。

您可以在 Java 方法中使用用户定义的 Java 节点(UDN)API,前提是要遵守Java 例程的局限性中描述的限制。关于使用 UDN API 的更多信息,请参阅编译 Java 用户定义的节点

Java 例程示例 1

该例程包含三个不同方向的参数并返回整数,该整数映射到 Java 返回类型 java.lang.Long

CREATE FUNCTION myProc1( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
 RETURNS INTEGER
 LANGUAGE JAVA
 EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod1";

可使用以下 ESQL 调用 myProc1

CALL myProc1( intVar1, intVar2, intVar3) INTO intReturnVar3;
-- or 
SET intReturnVar3 = myProc1( intVar1, intVar2, intVar3);

Java 例程示例 2

该例程包含三个不同方向的参数,并且 Java 返回类型为 void

CREATE PROCEDURE myProc2( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
 LANGUAGE JAVA
 EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod2";

必须使用以下 ESQL 调用 myProc2

CALL myProc2(intVar1, intVar2, intVar3);

以下 Java 类为上述每个 Java 示例提供了一个方法:

package com.ibm.broker.test;

class MyClass {
public static Long myMethod1( Long P1, Long[] P2 Long[] P3) { ... }
public static void myMethod2( Long P2, Long[] P2 Long[] P3) { ... }

 /* When either of these methods is called:
    P1 may or may not be NULL (depending on the value of intVar1).
    P2[0] is always NULL (whatever the value of intVar2).
    P3[0] may or may not be NULL (depending on the value of intVar3).
    This is the same as with LANGUAGE ESQL routines.
    When these methods return:
         intVar1 is unchanged
         intVar2 may still be NULL or may have been changed
         intVar3 may contain the same value or may have been changed.
     This is the same as with LANGUAGE ESQL routines.
     
    When myMethod1 returns: intReturnVar3 is either NULL (if the
    method returns NULL) or it contains the value returned by the
    method.
 */
}

ESQL 到 Java 数据类型映射

下表总结了 ESQL 到 Java 的映射。
注:
  • 仅 Java 标量包装器传递给 Java。
  • 根据过程参数的方向,ESQL 标量类型作为对象包装器或对象包装器数组映射到 Java 数据类型。每个包装器数组只包含一个元素。
  • 标量对象包装器允许在 Java 方法间传递 NULL 值。
ESQL 数据类型 1 Java IN 数据类型 Java INOUT 和 OUT 数据类型
INTEGER、INT java.lang.Long java.lang.Long []
FLOAT java.lang.Double java.lang.Double[]
DECIMAL java.math.BigDecimal java.math.BigDecimal[]
CHARACTER、CHAR java.lang.String java.lang.String[]
BLOB byte[] byte[][]
BIT java.util.BitSet java.util.BitSet[]
DATE com.ibm.broker.plugin.MbDate com.ibm.broker.plugin.MbDate[]
TIME 2 com.ibm.broker.plugin.MbTime com.ibm.broker.plugin.MbTime[]
GMTTIME 2 com.ibm.broker.plugin.MbTime com.ibm.broker.plugin.MbTime[]
TIMESTAMP 2 com.ibm.broker.plugin.MbTimestamp com.ibm.broker.plugin.MbTimestamp[]
GMTTIMESTAMP 2 com.ibm.broker.plugin.MbTimestamp com.ibm.broker.plugin.MbTimestamp[]
INTERVAL 不支持 不支持
BOOLEAN java.lang.Boolean java.lang.Boolean[]
REFERENCE(针对消息树)3 4 5 6 com.ibm.broker.plugin.MbElement com.ibm.broker.plugin.MbElement[] (支持 INOUT。不支持 OUT)
ROW 不支持 不支持
LIST 不支持 不支持
  1. 声明为 CONSTANT 的变量(或对声明为 CONSTANT 的变量的引用)不允许有方向 INOUT 或 OUT。
  2. Java 变量中设置的时区不重要;在输出 ESQL 中可以获得所需的时区。
  3. 引用参数在传递到 Java 方法时不能为 NULL。
  4. 引用参数在传递到 Java 方法时不能有方向 OUT。
  5. 如果 MbElement 作为 INOUT 参数从 Java 传回 ESQL,它必须指向消息树中的一个位置,传递到被调用 Java 方法的 MbElement 也指向此消息树。

    例如,如果对 OutputRoot.XML.Test 的 ESQL 引用作为 INOUT MbElement 传递到 Java 方法,但是调用返回时另一个 MbElement 被传回到 ESQL,那么此元素必须也指向 OutputRoot 树中的某个位置。

  6. 由于 ESQL 例程无法返回引用,因此使用 RETURNS 子句无法从 Java 方法返回 MbElement。但是如果符合上述第 5 点中所述的条件,MbElement 也可以作为 INOUT 方向参数返回。

如果引用所引用的变量数据类型与 Java 程序特征符中相应的数据类型相匹配,则可以在 Java 方法的 CALL 中使用对标量变量的引用。

Java 例程的局限性

以下局限性适用于从 ESQL 调用的 Java 例程:
  • 必须确保 Java 方法是线程安全(可重入)的。
  • 唯一允许的数据库连接是 4 类 JDBC 连接。此外,数据库操作不是代理事务的一部分;这意味着它们不受外部资源协调程序的控制(例如 XA 环境中的情况)。
  • 用户定义的 Java 节点(UDN)API 只应由调用 Java 方法的同一线程使用。

    您可以在自己的方法中衍生线程。但是,衍生的线程不得使用 Java 插件 API,并且您必须将控制权交还给代理。

    请注意,适用于 UDN API 用法的所有局限性也适用于从 ESQL 调用的 Java 方法。

  • 从 ESQL 调用的 Java 方法不得使用 MbNode 类。这意味着它们无法创建 MbNode 类型的对象,或者无法调用现有 MbNode 对象上的任何方法。
  • 如果要在从 ESQL 调用的 Java 方法中执行 MQ 或 JMS 操作,必须按照用户定义的节点(UDN)准则在 UDN 中执行 MQ 和 JMS 操作。

部署 Java 类

您可以在 Java 归档(JAR)文件中部署 Java 类。有两种方法可将 JAR 文件部署到代理中:
  1. 通过将它添加到代理归档(BAR)文件中

    将 JAR 文件添加到 BAR 文件,这是部署到代理的最高效、灵活的方法。

    您可以将 JAR 文件手动或使用工具自动添加到 BAR 文件中。使用工具将 JAR 文件添加到 BAR 文件是最简单的方法。

    如果工具在工作空间中打开的被引用 Java 项目中找到正确的 Java 类,它会自动将该 Java 类编译成 JAR 文件并添加到 BAR 文件中。这与您在 JAR 中部署 Java Compute 节点的过程相同,如用户定义的节点类装入中所述。

    使用工具部署 JAR 文件时,重新部署包含 JAR 文件的 BAR 文件会导致重新部署的流重新装入引用的 Java 类;就象停止并重新启动引用 Java 类的消息流一样。请确保停止并重新启动(或重新部署)所有引用待更新 JAR 文件的流。这样可以避免某些流与旧版本的 JAR 文件一起运行,而其他流与新版本的 JAR 文件一起运行产生的问题。

    请注意,使用工具将只部署 JAR 文件;它不部署单独的 Java 类文件。

  2. 通过将它放在以下的任一位置:
    1. 运行代理的机器上的 <Workpath>/shared-classes/ 文件夹
    2. 运行代理的机器上的 CLASSPATH 环境变量

    此过程必须手动完成;不能使用工具。

    使用这种方法时,重新部署消息流不会导致重新装入引用的 Java 类;停止并重新启动消息流也不会。在这种情况下,重新装入类的唯一方法是停止并重新启动代理自身。

要使代理能够找到 Java 类,请确保该类在上述任一位置中。如果代理找不到指定的类,它会抛出异常。

尽管在部署 JAR 文件时可以进行上述选择,但允许使用工具部署 BAR 文件,这样在重新部署 JAR 文件时最为灵活。

数据库例程

CREATE FUNCTION 不支持数据库例程。请使用 CREATE PROCEDURE 定义数据库例程。

相关概念
ESQL 概述
相关任务
正在开发 ESQL
调用存储过程
编译 Java 用户定义的节点
相关参考
语法图:可用类型
ESQL 语句
CALL 语句
ESQL 到 Java 的数据类型映射表
相关信息
../com.ibm.etools.mft.javanodeAPI.doc/com/ibm/broker/plugin/package-overview.html
声明 | 商标 | 下载 | 书库 | 支持 | 反馈
Copyright IBM Corporation 1999, 2006 最后一次更新时间:2006/08/14
ak04960_