随笔-25  评论-6  文章-0  trackbacks-0

在数据库应用程序中使用存储过程有许多好处,包括减少对网络的使用、提高性能以及降低开发成本。Java 存储过程是 DB2 支持的最流行的例程之一。原因之一是,由于 Java 编程语言非常流行,所以 Java 开发人员非常多。因此,在有多种语言可供选择时,Java 例程往往是首选的。

DB2 存储过程不一定非用 Java 来编写。如果业务逻辑只需要简单的存储过程,那么可以考虑用 SQL Procedure Language(SQL PL)进行存储过程开发。SQL 存储过程总是作为 受信任的 存储过程运行,并且因为它们不依赖于外部 Java 虚拟机(Java Virtual Machine,JVM)进程来装载过程,所以比 Java 例程快。

使用 Java 存储过程的好处与创建 Java 应用程序时获得的好处相同。Java 是非常安全的编程语言。用户只能获得 Java 字节码。Java 代码编译一次,就能够在支持 JVM 的任何计算机和操作系统上运行。因为 Java 代码在单独的 JVM 中运行,所以 JVM 能够正确地处理(可能导致 JVM 崩溃的)危险操作。不需要实现单独的基础设施来处理危险状况,因为 Java 具有捕获异常的内置机制。

在本文中,我们有时将 Java 存储过程称为例程。在 DB2 UDB 中存储过程和例程是同义的。在 DB2 V8 中引入了例程的概念,它既表示存储过程,也表示用户定义函数(UDF)。

本文讨论在开发或运行 Java 存储过程的过程中可能遇到的常见错误消息。在开始讨论之前,我们先讨论 Java 存储过程开发的重要概念和配置参数。接下来,描述如何启用 DB2 Java。需要建立 Java 环境才能成功地调用 Java 存储过程。





回页首


关键概念

以下概念对于理解存储过程在 DB2 环境中如何工作非常重要:

  • FENCED 或 NOT FENCED: 这个子句指定例程是否被认为可以在数据库管理器操作环境的进程或地址空间中“安全地”运行。

    如果存储过程被注册为 FENCED,那么数据库管理器就禁止过程访问它的内部资源(比如数据缓冲区)。大多数例程都有作为 FENCED 或 NOT FENCED 运行的选项。但是,Java 例程只能注册为 FENCED。一般来说,作为 FENCED 运行的例程执行得没有作为 NOT FENCED 运行的相似例程那么快。这是因为 NOT FENCED 例程可以在数据库引擎内利用进程间通信(IPC)。

    对没有经过彻底测试的例程使用 NOT FENCED,可能会破坏 DB2 完整性。DB2 对于许多常见的意外故障类型采取了某些保护措施,但是在使用 NOT FENCED 例程时无法保证完全的完整性。NOT FENCED 例程常常被称为受信任的(trusted)。声明为受信任的例程在数据库管理器的地址空间中运行。

    将例程注册为 NOT FENCED 需要 SYSADM 特权、DBADM 特权或一个特殊的特权(CREATE_NOT_FENCED)。定义为 NOT THREADSAFE 的例程只能指定 FENCED。

  • THREADSAFE 或 NOT THREADSAFE: 这个子句指定这个例程是否可以安全地在其他例程的进程中执行(THREADSAFE 是可以,NOT THREADSAFE 是不可以)。

    如果过程定义为 THREADSAFE,数据库管理器就可以在其他例程的进程中调用这个过程。一般来说,要想定义为 THREADSAFE,例程不应该使用任何全局或静态数据区域。许多编程参考资料讨论了如何编写线程安全的例程。FENCED 和 NOT FENCED 过程都可以是 THREADSAFE 的。

    如果过程定义为 NOT THREADSAFE,数据库管理器就绝不会在其他例程的进程中调用这个过程。

    在 Java 存储过程中,THREADSAFE 是默认的,无论它被声明为 FENCED 还是 NOT FENCED 存储过程。





回页首


配置参数

DB2 有许多配置参数。一些参数在数据库级上定义,其他参数在数据库管理级上定义。影响存储过程行为的大多数参数是在实例级(即数据库管理级)上定义的。

  • KEEPFENCED: 这是一个数据库管理器配置(DBM CFG)参数。在以前的 DB2 UDB 版本中,它被称为 KEEPDARI。这个参数指出,在完成一个防护模式例程调用之后,是否保留防护模式进程(db2fmp)。防护模式进程是作为单独的系统实体创建的,以便将用户编写的防护模式代码与数据库管理器代理进程隔离开来。这个参数只能应用于数据库服务器。在开发存储过程时,强烈建议将这个参数设置为 NO,这样,调用存储过程时总会得到全新的存储过程副本。如果存储过程常常重新编译,那么这特别重要。在生产环境中,应该将这个参数设置为 YES,因为它会显著地影响性能。NOT FENCED 存储过程不受这个配置参数影响,因为它们不在 db2fmp 进程中运行。
  • FENCED_POOL: 这是一个数据库管理器配置(DBM CFG)参数。它代表系统上缓存的空闲防护模式进程(db2fmp)的数量。对于线程化的 db2fmp 进程(为线程安全的存储过程和 UDF 提供服务的进程),这个参数代表每个 db2fmp 进程中缓存的线程数量。对于非线程化的 db2fmp 进程,这个参数代表缓存的进程数量。
  • NUM_INITFENCED: 这是一个数据库管理器配置(DBM CFG)参数。这个参数表示在 DB2START 时在 db2fmp 池中创建的非线程化空闲 db2fmp 进程的初始数量。如果没有指定 KEEPFENCED,这个参数就被忽略。
  • JDK_PATH: 这是一个数据库管理器配置(DBM CFG)参数。这个参数指出用来执行 Java 存储过程的 JVM 或 Java Development Kit(JDK)的位置。这是一个非常重要的参数。它的值应该设置为包含 JVM Java 可执行文件的 “bin” 目录的上一级目录的完整路径。在 Windows? 平台上的一个例子是 C:\Program Files\IBM\SQLLIB\java\jdk。UNIX? 例子是 /usr/java1.3.1。JVM 级别也非常重要,因为根据使用的 db2level 和平台级别,DB2 UDB 只支持某些 JVM 级别。(这个问题将在本文稍后讨论。)
  • JAVA_HEAP_SZ: 这是一个数据库管理器配置(DBM CFG)参数。这个参数决定为 Java 存储过程和 UDF 服务的 Java 解释器所使用的堆的最大大小。为了避免在 Java 存储过程中耗尽内存,可以增加这个值。但是,如果在环境中要调用许多存储过程(即,每个 JVM 都会分配这么多堆空间),那么分配太多内存也是有害的。一般规则是保持 JAVA_HEAP_SZ 为默认设置,即 512(4K 页)。
  • ASLHEAPSZ: 这是一个数据库管理器配置(DBM CFG)参数。应用程序支持层堆是本地应用程序和与它相关联的代理之间的通信缓冲区。这个缓冲区作为每个数据库管理器代理共享的内存分配。这个参数确定缓冲区的大小,用于在例程和发出调用的应用程序之间传递参数。存储过程中的参数数量和参数大小明确地影响这个配置参数。系统上允许同时存在的 db2fmp 进程的最大数量也受这个参数的影响。
  • QUERY_HEAP_SZ: 这是一个数据库管理器配置(DBM CFG)参数。这个参数指定可以分配给查询堆的最大内存量。查询堆用于在代理的私有内存中存储每个查询。每个查询的信息包括输入和输出 SQLDA、语句文本、SQLCA、包名、创建者、区号和一致性符号。提供这个参数是为了确保应用程序不会不必要地消耗代理中的大量虚拟内存。如果这个参数设置得过低,那么执行复杂 SQL 的存储过程会导致 db2fmp 进程意外终止。
  • DB2_FMP_COMM_HEAPSZ: 这是一个 db2set 注册表参数。这个参数可应用于所有平台,只有 AIX 32 位平台除外,在这种平台上这个值预定义为 256MB。这个变量指定防护例程调用(比如存储过程或用户定义函数调用)所使用的池的大小(以 4 KB 页为单位)。每个防护例程所使用的空间是 ASLHEAPSZ 配置参数值的两倍。如果在系统上运行大量防护例程,那么可能需要增加这个变量的值。如果运行的防护例程很少,可以降低这个值。将这个值设置为 0 就表示不创建池,因此不能调用防护例程。可以用以下公式计算系统上可以同时运行的 db2fmp 进程的数量:
    
    
    Maximum Number of db2fmps = DB2_FMP_COMM_HEAPSZ / (2*ASLHEAPSZ)





回页首


设置 Java 环境

需要执行几个步骤,然后才能编译 Java 存储过程。本节讨论设置用于运行 Java 过程的系统所需的步骤。

兼容的 JDK/JVM 级别

在开始之前,首先确保数据库服务器上安装了兼容的 JDK/JVM。每种操作系统支持不同的 JDK 级别。如果数据库实例配置为 64 位而不是 32 位,那么这特别重要。

支持的 JDK/JVM 级别的兼容性表可以在以下网页上找到:http://www-306.ibm.com/software/data/db2/udb/ad/v8/java/

同一个系统上可以安装多个 JVM。为了决定在执行 Java 存储过程时使用哪个 JVM,DB2 读取 JDK_PATH 数据库管理器配置参数。需要确保 JDK_PATH 指向与环境兼容的 JVM。

设置 Java 环境

DB2 数据库服务器的平台需要正确地设置才能使用 Java。对于 Java 支持,每种平台可能有自己的需求。

对于 UNIX 平台一般的 Java 设置需求可以在以下网页上找到:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0004675.htm

对于 Windows 平台一般的 Java 设置需求可以在以下网页上找到:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0006428.htm

HPUX 和 Linux

HPUX 和 Linux 对于 Java 支持有额外的需求:

防护 id

为了执行 FENCED 存储过程,DB2 通过防护 id 方式提供了额外的安全层。这个 id(和组)应该在创建 DB2 实例时创建。可以从以下网页获得关于这个 id 的更多信息:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/admin/t0005077.htm

配置参数

前面的 配置参数 小节提到了一组重要的配置参数。大多数这些参数可以保持默认设置。但是,当遇到问题(特别是性能问题或内存问题)时,应该检查和调整 DB2 配置参数,使之适合您的系统,这是非常重要的。

数据库特权

在大多数情况下,应用程序开发人员将开发存储过程。这一般意味着 DB2 管理员可能必须向应用程序开发人员提供必需的特权,让他们能够创建和维护这些存储过程。对于 Java 存储过程开发人员,可考虑提供以下特权:EXECUTE、CREATE_EXTERNAL_ROUTINE、CREATE_NOT_FENCED_ROUTINE、IMPLICIT_SCHEMA、CREATEIN 和 BINDADD。关于数据库特权的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/admin/c0005524.htm





回页首


创建和部署 Java 例程

设置好环境之后,就应该能够创建和部署自己的 Java(或 SQLJ)存储过程。DB2 Application Development Client(如果安装了)包含一组示例,您在初次创建存储过程时可以参考这些示例。Java 存储过程示例位于 sqllib/samples/java/jdbc 目录中,称为 SpServer.java。SQLJ 存储过程示例位于 sqllib/samples/java/sqlj 目录,称为 SpServer.sqlj。

编写自己的例程

在编写自己的存储过程时需要考虑一些事情。需要决定为存储过程采用哪种参数传递技术。DB2 UDB 对于 Java 应用程序支持两种参数风格:

  • PARAMETER STYLE JAVA —— 这意味着存储过程将使用符合 Java 语言和 SQLJ 例程规范的参数传递约定。IN/OUT 和 OUT 参数将作为单项数组传递,以便于返回值。这只能在使用 LANGUAGE JAVA 时指定。PARAMETER STYLE JAVA 过程不支持 DBINFO 或 PROGRAM TYPE 子句。
  • PARAMETER STYLE DB2GENERAL —— 这意味着存储过程将使用为 Java 方法定义的参数传递约定。这只能在使用 LANGUAGE JAVA 时指定。PARAMETER STYLE DB2GENERAL 仍然可以用于在 Java 例程中启用以下特性的实现:表函数、scratchpad、对 DBINFO 结构进行访问以及对函数或方法进行 FINAL CALL(和单独的首次调用)。为了使用 DB2GENERAL 参数风格,需要确保存储过程的类扩展了 COM.ibm.db2.app.StoredProc。关于 PARAMETER STYLE DB2GENERAL 的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/c0000420.htm

在 CREATE 语句中使用 PARAMETER STYLE JAVA 子句注册 Java 例程。

与其他任何应用程序一样,存储过程需要一个 Connection context。在 Java 和 JDBC 中,这是使用来自 java.sql.* 类的 Connection 对象完成的。调用存储过程的应用程序将建立连接。所以在 Java 存储过程中,以默认连接 方式建立连接,如清单 1 所示。


清单 1. 一个名为 INSERT_JAVASP 的示例存储过程
																				



1   //The simplest JAVA SP

2   import java.sql.*;

3 

4   public class INSERT_JAVASP

5   {

6     public static void iNSERT_JAVASP  (String input) throws SQLException,

 	Exception

7     {

8       int errorCode;

9  

10      try

11      {

12        // get caller's connection to the database

13   Connection con = DriverManager.getConnection("jdbc:default:connection");

14     

15        String query = "INSERT INTO CWYLAW.StoreData (c) VALUES (?)";

16

17        PreparedStatement pstmt = con.prepareStatement(query);

18        pstmt.setString(1, input);

19        pstmt.executeUpdate();

20  

21      }   

22      catch (SQLException sqle)

23      {

24        errorCode = sqle.getErrorCode();

25        throw new SQLException( errorCode   " FAILED" ); 

26      }

27    }

28  }
																		

在清单 1 的第 13 行上,Connection 对象(con)被建立为 “默认” 连接。调用存储过程的应用程序将在调用过程之前建立这个连接。使用默认连接时,存储过程从调用者那里获得它的连接属性。这里显示的例子是一个 Java 存储过程,它接受一个输入参数并且将其值插入 CWYLAW.StoreData 表。

对于使用例程的限制

对于为 DB2 UDB 开发存储过程,有几个限制。一定要检查 DB2 Infocenter 的以下部分,确保自己了解这些限制:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/c0009198.htm

Java 类型映射

Java 支持自己的一组数据类型。DB2 也有自己的一组数据类型。例如,DB2 数据类型 VARCHAR 在 Java 中不存在。但是,Java 有一个 String 对象,可以替代这种数据类型。DB2 UDB 有一组 “首选的” 数据类型映射,适用于 Java 应用程序和存储过程。表 1(引用于 Infocenter —— 参见 参考资料)显示了这些映射。

Java 数据类型映射
SQL 类型 JDBC 2.0 类型 Java 类型
BIGINT BIGINT long
BLOB BLOB java.sql.Blob
CHAR CHAR String
CHAR FOR BIT DATA BINARY byte[]
CLOB CLOB java.sql.Clob
DATE DATE java.sql.Date
DBCLOB CLOB java.sql.Clob
DECIMAL DECIMAL java.math.BigDecimal
DOUBLE DOUBLE double
FLOAT FLOAT double
INTEGER INTEGER int
GRAPHIC CHAR String
LONG VARCHAR LONGVARCHAR String
LONG VARCHAR FOR BIT DATA LONGVARBINARY byte[]
LONGVARGRAPHIC LONGVARCHAR String
NUMERIC NUMERIC java.math.BigDecimal
REAL REAL float
SMALLINT SMALLINT short
TIME TIME java.sql.Time
TIMESTAMP TIMESTAMP java.sql.Timestamp
VARCHAR VARCHAR String
VARCHAR FOR BIT DATA VARBINARY byte[]
VARGRAPHIC VARCHAR String

编译例程

创建了存储过程之后,需要对它进行编译。使用系统上安装的 JDK,用以下命令对过程进行编译:javac INSERT_JAVASP.java

这生成一个类文件。可以将这个类文件转移到 sqllib/function 目录(这是 DB2 获得存储过程可执行文件的默认位置),也可以将它转移到您选择的另一个位置(并且在 CREATE PROCEDURE 命令中使用这个定制的路径)。

另一个办法是将类文件打包到 JAR 文件中,并且部署 JAR 文件。可以使用以下命令将类文件打包到 JAR 文件中:jar -cvf INSERT_JAVASP.jar INSERT_JAVASP.class

关于放置 Java 类的位置的更多信息,请阅读 Infocenter 的以下部分:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/c0006348.htm.

SQLJ 例程

在编写 SQLJ 存储过程时,需要执行两个额外的步骤:

  • 使用 DB2 SQLJ Translator 翻译 SQLJ 源代码。这会将 sqlj 代码转换成 java 代码,并且创建一个 SQLJ 可序列化(.ser)文件。
  • 定制这个可序列化文件,使嵌入语句的访问计划被存储到包中(或绑定文件中)。这需要使用 db2sqljcustomize 命令。

关于 SQLJ 的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0007588.htm

数据库服务器上的 JAR 文件管理

如果使用 JAR 文件而不是类文件,就需要采取一些额外的步骤,使 DB2 认识到 JAR 文件是存储过程类的容器。DB2 附带四个内置的存储过程,帮助管理 JAR 文件。

  • SQLJ.INSTALL_JAR :这将把 JAR 文件 “安装” 到 DB2 中,这样,当 DB2 类装载器寻找要装载的存储过程库时,它会找到并且装载这个存储过程(而不是同名的另一个存储过程)。
    语法:CALL sqlj.install_jar( jar-url, jar-id )
  • SQLJ.REPLACE_JAR:这将用一个新副本 “替换” DB2 中的 JAR 文件。如果存储过程近来由于任何变更而重新编译过,那么这个操作特别有用。这样,DB2 类装载器将在运行时重新装载具有新内容的 JAR 文件并且使用新内容。
    语法:CALL sqlj.replace_jar( jar-url, jar-id )
  • SQLJ.REMOVE_JAR:这将从 DB2 实例中 “删除” JAR 文件。如果您打算删除存储过程并且不会重新创建它,那么这个操作就有用了。这样,DB2 就不会在内存中保留这个 JAR 文件的副本。
    语法:CALL sqlj.remove_jar( jar-id )
  • SQLJ.REFRESH_CLASSES:这将在 DB2 实例中 “刷新” 一个 JAR 文件中包含的所有类。当更新 Java 例程类时,需要这样做。这使 DB2 装载新的类。如果没有使用这个命令,DB2 将使用类的旧版本。这可以与 SQLJ.REPLACE_JAR 结合使用。
    语法:CALL sqlj.refresh_classes( void )

关于 JAR 文件管理的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/r0006425.htmhttp://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0006410.htm

注册例程

在编译了存储过程(并且可选地将它存储在 JAR 文件中),并将它转移到某个位置之后,可以对存储过程进行注册,使应用程序可以引用它。

为此,需要使用 CREATE PROCEDURE 语句。在 CREATE PROCEDURE 语句中,可以为存储过程指定几个选项。下面是其中几个重要的选项:

  • SPECIFIC:这在 DB2 编目中惟一地标识存储过程名。一般来说,“SPECIFIC” 名称匹配存储过程名。
  • DYNAMIC RESULT SETS:这决定存储过程是否返回一个结果集。存储过程也可能返回多个结果集。这个选项决定过程将返回多少结果集。
  • LANGUAGE:这应该设置为 JAVA。对于 SQLJ 存储过程,也使用 JAVA。
  • EXTERNAL NAME:这个参数决定某个存储过程的类文件或 JAR 文件的位置以及文件内的方法。文件的默认位置是 sqllib/function 文件夹。也可以指定文件实际位置的完整路径。EXTERNAL NAME 子句的格式如下: ‘jar-id!class_id.method_id’‘class_id.method_id’
  • FENCED / NOT FENCED:这个参数决定存储过程是声明为 FENCED,还是 NOT FENCED。只有在您认为代码能够安全执行的情况下,才应该使用 NOT FENCED 存储过程。
  • THREADSAFE / NOT THREADSAFE:这个参数决定 FENCED 存储过程的执行是否是线程安全的。这只对定义为 FENCED 的过程起作用,因为 NOT FENCED 过程总被定义为 THREADSAFE。
  • PARAMETER STYLE:对于 Java 例程,参数风格只能是 PARAMETER STYLE JAVA 或 PARAMETER STYLE DB2GENERAL。

关于 CREATE PROCEDURE 语句的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/admin/r0008328.htm

清单 2 显示一个 CREATE PROCEDURE 语句示例,其中使用了一些选项。


清单 2. CREATE PROCEDURE 语句示例
																				



CREATE PROCEDURE INSERT_JAVASP (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT_JAVASP!iNSERT_JAVASP'

;
																		

调用例程

一旦对存储过程进行了注册,首先要做的是调用它,确保它按照预期和设计进行工作。DB2 有一个 “CALL” 命令,可以使用它调用任何存储过程。在大多数情况下,希望让应用程序使用参数标志等动态地调用存储过程。请阅读 Application Development Guide,以便确定从应用程序中调用过程的最佳方式。

然而,DB2 命令行处理程序(CLP)可以使用以下语法调用存储过程:
CALL proc-name( [parm1, parm2...] )
其中的 parm1、parm2 等等是参数。如果参数是基于字符的输入参数,那么在单引号中指定字面值。如果参数是基于数值的输入参数,那么按原样指定字面值。如果参数是输出参数,那么使用‘?’字符表示输出参数。例如:

																				

$ db2 "CALL SHAKEBS.TESTPROC('hello', 'world', 1, 2.5, ?, 'testing')"
																		

在这个例子中,一共有 6 个参数。第一个、第二个和第六个参数都是字符字面值。第三个参数是数值字面值,适用于 integer 或 smallint 这样的数据类型。第四个参数也是数值字面值,但是因为它包含小数点,所以应该被看作 double、float 或 decimal 类型。因为第五个参数是问号(?),所以它表示输出参数。因此,当这个存储过程被调用时,它将在这个输出参数中返回一个值。

关于 “CALL” 命令的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0011378.htmhttp://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0009000.htmhttp://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0007055.htm





回页首


常见问题

现在,让我们看看应用程序开发人员在开发和执行 Java(或 SQLJ)存储过程时可能遇到的一些常见问题。本文的 下载 小节中的一个 zip 文件提供了所有例子。请注意,这些例子中大部分都要求在服务器上的数据库管理器配置文件中设置 KEEPFENCED=NO

SQL4301 RC=0

清单 3 显示第一个错误的例子,sqlcode 为 SQL4301,返回码为 0。


清单 3. SQL4301 rc=0 示例 1:Windows 上的 INSERT_JAVASP.java
																				



D:\>javac INSERT_JAVASP.java



D:\>copy INSERT_JAVASP.class "C:\Program Files\IBM\SQLLIB\Function"

        1 file(s) copied.



D:\>db2 -tvf Create.ddl

CREATE PROCEDURE INSERT_JAVASP (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT_JAVASP!iNSERT_JAVASP'



DB20000I  The SQL command completed successfully.



D:\>db2 call INSERT('D')

SQL4301N  Java or .NET interpreter startup or communication failed, reason

code "0".  SQLSTATE=58004
																		

这个操作失败并且显示 SQL4301 rc=0 错误消息。为什么呢?检查 JDK_PATH 数据库管理器配置参数的设置是否正确。JDK_PATH 应该设置为用来执行 Java 存储过程的 JVM/JDK 的 “bin” 的上一级目录。为了纠正这个问题,检查数据库管理器配置参数 JDK_PATH,并且修改它。


清单 4. SQL4301 rc=0 示例 1:数据库管理器配置文件的片段
																				



D:\>db2 get dbm cfg 



          Database Manager Configuration



     Node type = Enterprise Server Edition with local and remote clients



Database manager configuration release level         = 0x0a00



Maximum total of files open            (MAXTOTFILOP) = 16000

CPU speed (millisec/instruction)          (CPUSPEED) = 9.368161e-007

Communications bandwidth (MB/sec)   (COMM_BANDWIDTH) = 1.000000e 002



Max number of concurrently active databases  (NUMDB) = 8

Data Links support                       (DATALINKS) = NO

Federated Database System Support        (FEDERATED) = NO

Transaction processor monitor name     (TP_MON_NAME) =



Default charge-back account        (DFT_ACCOUNT_STR) =



Java Development Kit installation path    (JDK_PATH) = C:\PROGRA~1\IBM\

SQLLIB\java



...
																		

注意,JDK_PATH 没有指向 “bin” 的上一级目录。这需要修改,如清单 5 所示。


清单 5. SQL4301 rc=0 示例 1:更新数据库管理器配置文件
																				



D:\>db2 update dbm cfg using JDK_PATH C:\PROGRA~1\IBM\SQLLIB\java\jdk



DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command 

completed successfully.





D:\>db2stop force

09/25/2005 14:33:16     0   0   SQL1064N  DB2STOP processing was successful.

SQL1064N  DB2STOP processing was successful.



D:\>db2start

09/25/2005 14:33:46     0   0   SQL1063N  DB2START processing was successful.

SQL1063N  DB2START processing was successful.



D:\>db2 connect to sample





   Database Connection Information



 Database server        = DB2/NT 8.2.3

 SQL authorization ID   = SHAKEBS

 Local database alias   = SAMPLE



D:\>db2 call INSERT_JAVASP('D')



  Return Status = 0



D:\>db2 "select * from CWYLAW.StoreData"



C

---

D



  1 record(s) selected.
																		

清单 6 显示 SQL4301 rc=0 错误的另一个例子。这是由于使用了不兼容的 JVM 造成的。64 位实例需要 64 位的 JDK。32 位实例需要 32 位的 JDK。


清单 6. SQL4301 rc=0 示例 2:在 AIX 上收到 SQL4301 rc=0 错误
																				



$ which java

/wsdb/v81/bldsupp/AIX/jdk1.4.1/bin/java



$ java -version



java version "1.4.1"

Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1)

Classic VM (build 1.4.1, J2RE 1.4.1 IBM AIX build ca1411-20030930 (JIT enabled:

jitc))



$ db2level



DB21085I  Instance "dbguest4" uses "64" bits and DB2 code release 

"SQL08022" with level identifier "03030106".

Informational tokens are "DB2 v8.1.1.88", "s050422", "U800789", and FixPak "9".

Product is installed at "/usr/opt/db2_08_01".





$ db2 connect to sample



   Database Connection Information



 Database server        = DB2/AIX64 8.2.2

 SQL authorization ID   = DBGUEST4

 Local database alias   = SAMPLE



$ db2 "call out_language(?)"

SQL4301N  Java or .NET interpreter startup or communication failed, reason

code "0".  SQLSTATE=58004
																		

一旦使用了适合平台的 JDK 级别,这个错误就应该消失了。


清单 7. SQL4301 rc=0 示例 2:在 AIX 上纠正 SQL4301 rc=0 错误
																				



$ db2 connect to sample



   Database Connection Information



 Database server        = DB2/AIX64 8.2.2

 SQL authorization ID   = DBGUEST4

 Local database alias   = SAMPLE



$ db2 "call out_language(?)"



  Value of output parameters

  --------------------------

  Parameter Name  : LANGUAGE

  Parameter Value : JAVA



  Return Status = 0



$ which java

/wsdb/v81/bldsupp/AIX5L64/jdk1.4.1/bin/java



$ java -version

java version "1.4.1"

Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1)



Classic VM (build 1.4.1, J2RE 1.4.1 IBM AIX 5L for PowerPC (64 bit JVM) 

build ca

ix641411-20030930 (JIT enabled: jitc))
																		

常用做法是检查 db2diag.log 中的重要错误消息,由此可以判断出使用的 JDK 级别不正确。


清单 8. SQL4301 rc=0 示例 2:db2diag.log 中的相关条目
																				



2005-10-02-18.42.36.052560-240 E226800A732        LEVEL: Error (OS)

PID     : 191200               TID  : 1           PROC : db2fmp

INSTANCE: dbguest4             NODE : 000

FUNCTION: DB2 UDB, oper system services, sqloLoadModule, probe:130

CALLED  : OS, -, dlopen

OSERR   : ENOEXEC (8) "Cannot run a file that does not have a valid format."

MESSAGE : Attempt to load specified library failed.

DATA #1 : Library name or path, 55 bytes

/wsdb/v81/bldsupp/AIX/jdk1.4.1/jre/bin/classic/libjvm.a

DATA #2 : shared library load flags, PD_TYPE_LOAD_FLAGS, 4 bytes

2

DATA #3 : String, 145 bytes



	0509-022 Cannot load module 

   /wsdb/v81/bldsupp/AIX/jdk1.4.1/jre/bin/classic/libjvm.a.

	0509-124 The program is a discontinued 64-bit object file.



2005-10-02-18.42.36.053802-240 E227533A860        LEVEL: Error (OS)

PID     : 191200               TID  : 1           PROC : db2fmp

INSTANCE: dbguest4             NODE : 000

FUNCTION: DB2 UDB, oper system services, sqloLoadModule, probe:140

CALLED  : OS, -, dlopen

OSERR   : ENOEXEC (8) "Cannot run a file that does not have a valid format."

MESSAGE : Attempt to load specified library augmented with object name failed.

DATA #1 : Library name or path, 65 bytes

/wsdb/v81/bldsupp/AIX/jdk1.4.1/jre/bin/classic/libjvm.a(shr_64.o)

DATA #2 : shared library load flags, PD_TYPE_LOAD_FLAGS, 4 bytes

262146

DATA #3 : String, 231 bytes

	0509-022 Cannot load module 

	   /wsdb/v81/bldsupp/AIX/jdk1.4.1/jre/bin/classic/libjvm.a(shr_64.o).

	0509-153   File /wsdb/v81/bldsupp/AIX/jdk1.4.1/jre/bin/classic/libjvm.a is 

	    not an archive or the file could not be read properly.



2005-10-02-18.42.36.058868-240 I228394A367        LEVEL: Error

PID     : 191200               TID  : 1           PROC : db2fmp

INSTANCE: dbguest4             NODE : 000

FUNCTION: DB2 UDB, oper system services, sqloJVMstart, probe:30

MESSAGE : sqloloadmodule failed.  RC:

DATA #1 : Hexdump, 4 bytes

0x0FFFFFFFFFFFC080 : 870F 009B                                  ....



2005-10-02-18.42.36.059205-240 I228762A362        LEVEL: Error

PID     : 191200               TID  : 1           PROC : db2fmp

INSTANCE: dbguest4             NODE : 000

FUNCTION: DB2 UDB, oper system services, sqloJAttach, probe:5

MESSAGE : JVM startup failed.  RC:

DATA #1 : Hexdump, 4 bytes

0x0FFFFFFFFFFFF3E0 : FFFF EF34                                  ...4



2005-10-02-18.42.36.059511-240 I229125A363        LEVEL: Error

PID     : 191200               TID  : 1           PROC : db2fmp

INSTANCE: dbguest4             NODE : 000

FUNCTION: DB2 UDB, BSU Java support, sqlejAttach, probe:10

MESSAGE : Error from sqloJAttach.  RC:

DATA #1 : Hexdump, 4 bytes

0x0FFFFFFFFFFFF4A0 : FFFF EF34                                  ...4



2005-10-02-18.42.36.060331-240 I229489A372        LEVEL: Severe

PID     : 251500               TID  : 1           PROC : db2agent (SAMPLE)

INSTANCE: dbguest4             NODE : 000         DB   : SAMPLE

APPHDL  : 0-7                  APPID: *LOCAL.dbguest4.051002224226

FUNCTION: DB2 UDB, routine_infrastructure, sqlerGetFmpThread, probe:20

RETCODE : ZRC=0xFFFFFBEE=-1042



2005-10-02-18.42.36.066498-240 I229862A314        LEVEL: Warning

PID     : 124744               TID  : 1           PROC : db2sysc

INSTANCE: dbguest4             NODE : 000

MESSAGE : Removing FMP from pool

DATA #1 : Hexdump, 16 bytes

0x0FFFFFFFFFFFE090 : 0000 0000 0000 0000 0002 EAE0 0002 49B8    ...I.



2005-10-02-18.44.20.194287-240 I230177A348        LEVEL: Event

PID     : 120486               TID  : 1           PROC : db2flacc

INSTANCE: dbguest4             NODE : 000

FUNCTION: DB2 UDB, config/install, sqlfLogUpdateCfgParam, probe:30

CHANGE  : CFG DBM: "JDK_path" From: "/wsdb/v81/bldsupp/AIX/jdk1.4.1" To: 

    "/wsdb/v81/bldsupp/AIX5L64/jdk1.4.1"
																		

SQL4301 RC=2

本文中没有为 SQL4301 RC=2 错误消息提供例子,但是这种错误也值得注意。前面在讨论为 Java 存储过程支持设置环境时提到过,Linux 和 HPUX 平台需要额外的步骤。如果没有执行这些额外步骤,就会发生 SQL4301 RC=2 错误。如果您使用这些平台,那么请确保正确地设置了环境。

SQL4301 RC=4


清单 9. SQL4301 rc=4 示例:Windows 上的 INSERT_JAVASP.java
																				



D:\>javac INSERT_JAVASP.java



D:\>copy INSERT_JAVASP.class "C:\Program Files\IBM\SQLLIB\Function"

        1 file(s) copied.     



D:\>db2 -tvf Create.ddl

CREATE PROCEDURE INSERT_JAVASP (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT_JAVASP!iNSERT_JAVASP'



DB20000I  The SQL command completed successfully.





D:\>db2 call INSERT('A')

SQL4301N  Java or .NET interpreter startup or communication failed, 

reason code "4".  SQLSTATE=58004
																		

这个操作失败并且显示 SQL4301 rc=4 错误消息。为什么呢?检查 JAVA_HEAP_SZ 数据库管理器配置参数是否足够大,足以容纳您的 Java 存储过程。JAVA_HEAP_SZ 的默认值(512 个 4KB 页)应该足够了,但是如果出现这种错误,可以尝试将这个值加倍。

关于这个参数的更多信息,请查阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/admin/r0000137.htm


清单 10. SQL4301 rc=4 示例:数据库管理器配置文件片段
																				



D:\>db2 get dbm cfg 



          Database Manager Configuration



     Node type = Enterprise Server Edition with local and remote clients



 Database manager configuration release level            = 0x0a00



 Maximum total of files open               (MAXTOTFILOP) = 16000

 CPU speed (millisec/instruction)             (CPUSPEED) = 9.368161e-007

 Communications bandwidth (MB/sec)      (COMM_BANDWIDTH) = 1.000000e 002



...



 Database monitor heap size (4KB)          (MON_HEAP_SZ) = 66

 Java Virtual Machine heap size (4KB)     (JAVA_HEAP_SZ) = 1

 Audit buffer size (4KB)                  (AUDIT_BUF_SZ) = 0

 Size of instance shared memory (4KB)  (INSTANCE_MEMORY) = AUTOMATIC

 Backup buffer default size (4KB)            (BACKBUFSZ) = 1024

 Restore buffer default size (4KB)           (RESTBUFSZ) = 1024



...


																		

JAVA_HEAP_SZ 为 1(4K 页)显然不够运行哪怕是最简单的存储过程。在大多数情况下,默认设置 512(4K 页)应该足够了。在很少见的情况下,可能仍然会出现这个错误消息,此时可以考虑将这个配置参数再加倍。


清单 11. SQL4301 rc=4 示例:更新数据库管理器配置文件
																				



D:\>db2 update dbm cfg using JAVA_HEAP_SZ 512

DB20000I  The UPDATE DATABASE MANAGER CONFIGURATION command completed

successfully.

  

D:\>db2stop force

09/25/2005 14:33:16     0   0   SQL1064N  DB2STOP processing was successful.

SQL1064N  DB2STOP processing was successful.



D:\>db2start

09/25/2005 14:33:46     0   0   SQL1063N  DB2START processing was successful.

SQL1063N  DB2START processing was successful.



D:\>db2 connect to sample



   Database Connection Information



 Database server        = DB2/NT 8.2.3

 SQL authorization ID   = SHAKEBS

 Local database alias   = SAMPLE



D:\>db2 call INSERT_JAVASP('A')



  Return Status = 0



D:\>db2 "select * from CWYLAW.StoreData"



C

---

A



  1 record(s) selected.
																		

SQL4301 RC=-4301


清单 12. SQL4301 rc=-4301 示例:Windows 上的 INSERT_JAVASP.java
																				



D:\>javac INSERT_JAVASP.java



D:\>copy INSERT_JAVASP.class "C:\Program Files\IBM\SQLLIB\Function"

        1 file(s) copied.

        

D:\>db2 -tvf Create.ddl

CREATE PROCEDURE INSERT_JAVASP (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT_JAVASP!iNSERT_JAVASP'



DB20000I  The SQL command completed successfully.





D:\>db2 call INSERT('D')

SQL4301N  Java or .NET interpreter startup or communication failed, 

reason code "-4301".  SQLSTATE=58004
																		

这个操作失败并且显示 SQL4301 rc=-4301 错误消息。为什么呢?检查环境变量 CLASSPATH,确保 db2java.zip 在 CLASSPATH 中。如果 db2java.zip 不在 CLASSPATH 中,就添加它。


清单 13. SQL4301 rc=-4301 示例:修改 Windows 上的环境变量 CLASSPATH
																				



D:>set | more



ALLUSERSPROFILE=C:\Documents and Settings\All Users

APPDATA=C:\Documents and Settings\Administrator\Application Data

CLASSPATH=.;C:\Progra~1\IBM\SQLLIB\java\db2jcc.jar;C:\Progra~1\IBM\SQLLIB\

java\sqlj.zip;C:\Progra~1\IBM\SQLLIB\java\common.jar;C:\Progra~1\IBM\SQLLIB\

java\db2jcc_license_cisuz.jar;C:\Progra~1\IBM\SQLLIB\java\db2jcc_license_cu.jar

...



D:\>set CLASSPATH=%CLASSPATH%;C:\Progra~1\IBM\SQLLIB\java\db2java.zip
																		

在这个例子中,我们在命令行上设置 CLASSPATH。这只对您登录进的用户会话有效。建议将它添加到全局环境中。在 Windows 上,可以使用 System Control Panel 来完成。在 UNIX 系统上,将 CLASSPATH 添加到用户帐户的 .profile 文件中。


清单 14. SQL4301 rc=-4301 示例:解决了 SQL4301 rc=-4301
																				



D:\>db2 call INSERT_JAVASP('D')



  Return Status = 0



D:\>db2 "select * from CWYLAW.StoreData"



C

---

D



  1 record(s) selected.
																		

SQL4302

SQL4302 常常意味着在 Java 存储过程代码中捕获了一个异常,或者发生了错误状况。应该检查 db2diag.log。在 DIAGLEVEL 3(默认设置)上,db2diag.log 捕获堆栈跟踪,甚至给出代码中捕获到异常处的行号。下面这个简单的例子假设编译了 Query.java 并且将 Query.class 文件复制到了 Windows 计算机的 \sqllib\FUNCTION 目录中。


清单 15. SQL4302 示例:Windows 上的 Query.java
																				

   

1    import java.sql.*;

2         

3    public class Query

4    {

5       public static void query ( int id , String[] s1 ) throws 

            SQLException, Exception

6      {

7           // Get connection to the database

8           Connection con = 

            DriverManager.getConnection("jdbc:default:connection");

9           PreparedStatement stmt = null;

10          String errorLabel =  null;

11          String sql;

12                  

13          

14          sql = "SELECT NAME FROM STAFF WHERE ID = ?";

15          stmt = con.prepareStatement( sql );     

16          stmt.setInt(1, id);   

17          ResultSet rs = stmt.executeQuery();

18                  

19          if (!rs.next()) {

20             // set errorCode to SQL0100 to indicate data not found

21             errorLabel = "SQL0100 : NO DATA FOUND, QUERY RETURNS 

               EMPTY RESULT SET";

22             throw new SQLException(errorLabel);

23          } else {

24             // move to first row of result set

25             s1[0] = rs.getString(1);                

26          }   

27                  

28                  

29          // clean up resources

30          rs.close();

31          stmt.close();

32          con.close();

33                           

34          }

35   }



D:\>db2 -tvf Create.ddl

CREATE PROCEDURE CWYLAW.QUERY (IN ID INT, OUT NAME CHAR(9))

SPECIFIC QUERY

DYNAMIC RESULT SETS 0

NOT DETERMINISTIC

LANGUAGE JAVA

EXTERNAL NAME 'Query.query'

FENCED

THREADSAFE

PARAMETER STYLE JAVA



DB20000I  The SQL command completed successfully.





D:\>db2 call query(5, ?)

SQL4302N  Procedure or user-defined function "CWYLAW.QUERY", 

specific name "QUERY" aborted with an exception "SQL0100 : 

NO DATA FOUND, QUERY RETURNS EMPTY RESULT".  SQLSTATE=38501



D:\>db2 call query(10, ?)



  Value of output parameters

  --------------------------

  Parameter Name  : NAME

  Parameter Value : Sanders



  Return Status = 0
																		

SQL4302 错误并不代表严重的错误。实际上,这是一个好信号。它意味着 Java 代码中的异常处理程序工作正常,并且捕获到了一个异常。在下面您将看到,db2diag.log 实际上告诉您在 Query.java 的第 22 行捕获了这个异常。在这个例子中 SQL4302 告诉我们,过程中的查询 SELECT NAME FROM STAFF WHERE ID = 5 返回一个空的结果集。如果提供一个有效的 ID(比如 10),那么这个存储过程将返回一个名称(在本例中是 Sanders)。


清单 16. SQL4302 示例:db2diag.log 中的相关条目
																				

   

2005-10-02-21.51.36.325000-240 I79282H396         LEVEL: Warning

PID     : 2140                 TID  : 2684        PROC : db2fmp.exe

INSTANCE: DB2                  NODE : 000

FUNCTION: DB2 UDB, BSU Java support, sqlejCallJavaRoutine_dll, probe:315

MESSAGE : Exception thrown during routine invocation:

DATA #1 : Hexdump, 4 bytes

0x01ACF5EC : D480 5501                                  ..U.



2005-10-02-21.51.36.335000-240 E79680H375         LEVEL: Warning

PID     : 2140                 TID  : 2684        PROC : db2fmp.exe

INSTANCE: DB2                  NODE : 000

FUNCTION: DB2 UDB, BSU Java support, sqlejLogException, probe:10

MESSAGE : ADM10000W  A Java exception has been caught.  The Java stack 

          traceback has been written to the db2diag.log.



2005-10-02-21.51.36.345000-240 I80057H475         LEVEL: Warning

PID     : 2140                 TID  : 2684        PROC : db2fmp.exe

INSTANCE: DB2                  NODE : 000

FUNCTION: DB2 UDB, BSU Java support, sqlejLogException, probe:10

DATA #1 : String, 112 bytes

java.sql.SQLException: SQL0100 : NO DATA FOUND, QUERY RETURNS EMPTY 

  RESULT SET at Query.query(Query.java:22)

DATA #2 : Hexdump, 4 bytes

0x01ACF424 : 0000 0000                                  ....



2005-10-02-21.51.36.355000-240 I80534H384         LEVEL: Warning

PID     : 2140                 TID  : 2684        PROC : db2fmp.exe

INSTANCE: DB2                  NODE : 000

FUNCTION: DB2 UDB, routine_infrastructure, sqlerJavaCallRoutine, probe:30

MESSAGE : Error from DB2ER CallUDF.  RC:

DATA #1 : Hexdump, 4 bytes

0x01ACF97C : 32EF FFFF                                  2...



2005-10-02-21.51.36.365000-240 I80920H959         LEVEL: Error

PID     : 3632                 TID  : 2840        PROC : db2bp.exe

INSTANCE: DB2                  NODE : 000

APPID   : *LOCAL.DB2.051003014530

FUNCTION: DB2 UDB, oper system services, sqlofica, probe:10

DATA #1 : Hexdump, 136 bytes

0x0012FC90 : 5351 4C43 4120 2020 8800 0000 32EF FFFF    SQLCA   ....2...

0x0012FCA0 : 4600 4357 594C 4157 2E51 5545 5259 FF51    F.CWYLAW.QUERY.Q

0x0012FCB0 : 5545 5259 FF53 514C 3031 3030 203A 204E    UERY.SQL0100 : N

0x0012FCC0 : 4F20 4441 5441 2046 4F55 4E44 2C20 5155    O DATA FOUND, QU

0x0012FCD0 : 4552 5920 5245 5455 524E 5320 454D 5054    ERY RETURNS EMPT

0x0012FCE0 : 5920 5245 5355 4C54 5351 4C45 4A45 5854    Y RESULTSQLEJEXT

0x0012FCF0 : 0000 0000 0000 0000 0000 0000 0000 0000    ................

0x0012FD00 : 0000 0000 0000 0000 2020 2020 2020 2020    ........        

0x0012FD10 : 2020 2033 3835 3031                           38501
																		

SQL4304 RC=1


清单 17. SQL4304 rc=1 示例:AIX 上的 SQL4304RC1.java
																				



$ javac SQL4304RC1.java



$ cp SQL4304RC1.class ~/sqllib/function



$ db2 -tvf CreateSP_wrong.ddl

CREATE PROCEDURE SQL4304RC1 (IN INPUT int)

SPECIFIC SQL4304RC1

DYNAMIC RESULT SETS 1

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE DB2GENERAL

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'SQ4304RC1!abend'



DB20000I  The SQL command completed successfully.





$ db2 "call SQL4304RC1(3)"

SQL4304N  Java stored procedure or user-defined function "SHAKEBS.SQL4304RC1",

specific name "SQL4304RC1" could not load Java class "SQ4304RC1", reason code

"1".  SQLSTATE=42724
																		

这个操作失败并且显示 SQL4304 rc=1 错误消息。为什么呢?注意,EXTERNAL NAME 子句中的类名拼写错了(它应该是 SQL4304RC1!abend,缺少了 “L”)。要纠正这个错误,应该删除这个过程,并且在 EXTERNAL NAME 子句中采用正确的拼写来重新创建它。


清单 18. SQL4304 rc=1 示例:纠正 SQL4304 rc=1 错误
																				



$ db2 drop procedure SQL4304RC1

DB20000I  The SQL command completed successfully.



$ db2 -tvf CreateSP.ddl

CREATE PROCEDURE SQL4304RC1 (IN INPUT int)

SPECIFIC SQL4304RC1

DYNAMIC RESULT SETS 1

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE DB2GENERAL

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'SQL4304RC1!abend'



DB20000I  The SQL command completed successfully.





$ db2 "call SQL4304RC1(3)"



  Result set 1

  --------------



  ID     NAME      DEPT   JOB   YEARS  SALARY    COMM

  ------ --------- ------ ----- ------ --------- ---------

     180 Abrahams      38 Clerk      3  12009.75    236.50

     230 Lundquist     51 Clerk      3  13369.80    189.65



  2 record(s) selected.



  Return Status = 0
																		

SQL4304 RC=2


清单 19. SQL4304 rc=2 示例:AIX 上的 SQL4304RC2.java
																				



$ javac SQL4304RC2.java



$ cp SQL4304RC2.class ~/sqllib/function



$ db2 -tvf CreateSP_wrong.ddl

CREATE PROCEDURE SQL4304RC2 (IN INPUT int)

SPECIFIC SQL4304RC2

DYNAMIC RESULT SETS 1

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE DB2GENERAL

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'SQL4304RC2!abend'



DB20000I  The SQL command completed successfully.



$ db2 "call SQL4304RC2(3)"

SQL4304N  Java stored procedure or user-defined function "SHAKEBS.SQL4304RC2",

specific name "SQL4304RC2" could not load Java class "SQL4304RC2", reason code

"2".  SQLSTATE=42724
																		

这个操作失败并且显示 SQL4304 rc=2 错误消息。为什么呢?因为 PARAMETER STYLE 是 DB2GENERAL,所以需要确保 Java 源代码扩展 COM.ibm.db2.app.StoredProc。为了纠正这个问题,将 extends COM.ibm.db2.app.StoredProc 添加到存储过程类名的末尾。


清单 20. SQL4304 rc=2 示例:SQL4304RC2.java
																				



1    //The simplest JAVA SP

2    import java.sql.*;

3    import COM.ibm.db2.app.*;

4     

5    public class SQL4304RC2 extends COM.ibm.db2.app.StoredProc

6    { 

7      public void abend  (int input) throws SQLException,Exception

8      {  

9        int errorCode;

10   

11       try

12       {

13         // get caller's connection to the database

14         Connection con = DriverManager.getConnection("jdbc:default:connection");

15     

16         String query = "SELECT * FROM STAFF where YEARS = ?";

17

18         PreparedStatement pstmt = con.prepareStatement(query);

19         ResultSet rs = null;

20         pstmt.setInt(1, input);

21         rs = pstmt.executeQuery();

22    

23       } 

24       catch (SQLException sqle)

25       {

26         errorCode = sqle.getErrorCode();

27         throw new SQLException( errorCode   " FAILED - "   sqle.getMessage()); 

28       }  

29     }

30   }
																		

代码中的第 5 行现在正确地扩展类。重新对代码进行编译,然后替换 sqllib/function 中的 .class 文件,并且重新执行存储过程。

注意:导致 SQL4304 rc=2 错误消息的另一个常见错误是,存储过程的主方法被声明为 “public static” 方法。PARAMETER STYLE DB2GENERAL 过程不能声明为 “static” 方法,像以上代码的第 7 行那样进行声明才是正确的。


清单 21. SQL4304 rc=2 示例:纠正 SQL4304 rc=2 错误
																				



$ javac SQL4304RC2.java



$ cp SQL4304RC2.class ~/sqllib/function



$ db2 "call SQL4304RC2(3)"



  Result set 1

  --------------



  ID     NAME      DEPT   JOB   YEARS  SALARY    COMM

  ------ --------- ------ ----- ------ --------- ---------

     180 Abrahams      38 Clerk      3  12009.75    236.50

     230 Lundquist     51 Clerk      3  13369.80    189.65



  2 record(s) selected.



  Return Status = 0
																		

SQL4306


清单 22. SQL4306 示例:INSERT.sqlj
																				

     

D:\>sqlj INSERT.sqlj

   

D:\>db2sqljcustomize -user cwylaw -password xxxxxxxxx -url 

   jdbc:db2://claw.torolab.ibm.com:50000/sample INSERT_SJProfile0

[ibm][db2][jcc][sqlj]

[ibm][db2][jcc][sqlj] Begin Customization

[ibm][db2][jcc][sqlj] Loading profile: INSERT_SJProfile0

[ibm][db2][jcc][sqlj] Customization complete for profile 

    INSERT_SJProfile0.ser

[ibm][db2][jcc][sqlj] Begin Bind

[ibm][db2][jcc][sqlj] Loading profile: INSERT_SJProfile0



[ibm][db2][jcc][sqlj] Driver defaults(user may override): BLOCKING ALL 

    VALIDATE BIND STATICREADONLY YES

[ibm][db2][jcc][sqlj] Fixed driver options: DATETIME ISO DYNAMICRULES BIND

[ibm][db2][jcc][sqlj] Binding package INSERT01 at isolation level UR

[ibm][db2][jcc][sqlj] Binding package INSERT02 at isolation level CS

[ibm][db2][jcc][sqlj] Binding package INSERT03 at isolation level RS

[ibm][db2][jcc][sqlj] Binding package INSERT04 at isolation level RR

[ibm][db2][jcc][sqlj] Bind complete for INSERT_SJProfile0



D:\>jar -cvf INSERT.jar *.class *.ser

added manifest

adding: INSERT.class(in = 1192) (out= 684)(deflated 42%)



D:\>db2 call sqlj.install_jar("file:///D:\INSERT.jar", 

'INSERTJAR')

DB20000I  The CALL command completed successfully.



D:\>db2 -tvf Create.ddl

CREATE PROCEDURE INSERT (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT.INSERT'



DB20000I  The SQL command completed successfully.



D:\>db2 call INSERT('abc')

SQL4306N  Java stored procedure or user-defined function "CWYLAW.INSERT",

specific name "INSERT" could not call Java method "INSERT", 

signature "(Ljava/lang/String;)V".  SQLSTATE=42724
																		

为什么会产生这个 SQL4306 错误?请看看源代码和 CREATE PROCEDURE 语句。注意,在 Java 代码中,方法被声明为:public static void iNSERT (String input)

注意小写字母‘i’。


清单 23. SQL4306 示例:Windows 上的 INSERT.sqlj
																				



//The simplest SQLJ SP

import java.sql.*;

import sqlj.runtime.*; 

import sqlj.runtime.ref.*; 

 

public class INSERT 

{

  public static void iNSERT (String input) throws SQLException, Exception

 

    { 

    	#sql { INSERT INTO CWYLAW.StoreData (c) VALUES (:input) }; 

    }  

}
																		


清单 24. SQL4306 示例:INSERT 过程所需的 StoreData 表的 CREATE TABLE 语句
																				



CREATE TABLE StoreData (c char(3));
																		

但是,在 CREATE PROCEDURE 语句中,EXTERNAL NAME 被声明为 EXTERNAL NAME 'INSERT.INSERT'。注意大写字母‘I’。所以,出现 SQL4306 错误的原因是源代码中的 Java 方法名与 CREATE PROCEDURE 语句中的 EXTERNAL NAME 不匹配。要纠正这个问题,就要确保 Java 方法与 CREATE PROCEDURE 语句中的 EXTERNAL NAME 子句精确匹配。在这个例子中,我们选择修改 CREATE PROCEDURE 语句而不是修改源代码。


清单 25. SQL4306 示例:INSERT 存储过程的正确的 CREATE PROCEDURE 语句
																				



CREATE PROCEDURE INSERT (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT.iNSERT'

;
																		

现在,INSERT 过程能够成功运行了。


清单 26. 运行 INSERT 过程
																				

D:\>db2 drop procedure INSERT

DB20000I  The SQL command completed successfully.



D:\>db2 call sqlj.remove_jar('INSERTJAR')

DB20000I  The CALL command completed successfully.



D:\>db2 call sqlj.install_jar("file:///D:\INSERT.jar", 'INSERTJAR')

DB20000I  The CALL command completed successfully.



D:\>db2 -tvf Create.ddl

CREATE PROCEDURE INSERT (IN INPUT CHAR(3))

SPECIFIC INSERT

DYNAMIC RESULT SETS 0

DETERMINISTIC

LANGUAGE JAVA

PARAMETER STYLE JAVA

NO DBINFO

FENCED

THREADSAFE

MODIFIES SQL DATA

PROGRAM TYPE SUB

EXTERNAL NAME 'INSERT.iNSERT'



DB20000I  The SQL command completed successfully.

D:\>db2 call INSERT('abc')



  Return Status = 0

  

D:\>db2 select * from StoreData



C

---

abc



  1 record(s) selected.
																		

还应该花点儿时间看看类型签名的 JVM 表示,因为这可以帮助您在遇到 SQL4306 错误时识别出问题。SQL4306 错误的另一个常见原因是过程的参数与 Java 方法定义不匹配。在这个例子中,SQL4306 错误的末尾包含一个含义有点儿模糊的错误符号:


清单 27. SQL4306 错误显示了 JVM 返回的 Java 类型
																				



SQL4306N  Java stored procedure or user-defined function "CWYLAW.INSERT",

specific name "INSERT" could not call Java method "INSERT", signature

"(Ljava/lang/String;)V".  SQLSTATE=42724
																		

(Ljava/lang/String;)V 实际上是 JVM 返回的 Java 类型签名。

表 2. Java VM 类型签名
类型签名 Java 类型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L fully-qualified-class ; fully-qualified-class
[ type type[]
( arg-types ) ret-type method type

所以这个错误消息指出,DB2 试图调用 Java 存储过程 INSERT,这个过程的 Java VM 类型签名是 (Ljava/lang/String;)V,其中 fully-qualified-classjava/lang/String(即,输入参数是类型 String),返回类型是 V,代表类型 void。这精确地匹配 Java 方法定义:

public static void iNSERT (String input)

现在,已经证实 Java VM 签名是正确的,所以确定了问题

posted on 2006-07-21 10:03 MyJavaWorld 阅读(5612) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: