解决 DB2 UDB Java 存储过程的常见问题(上)
2009-11-23 00:00:00 来源:WEB开发网简介
在数据库应用程序中使用存储过程有许多好处,包括减少对网络的使用、提高性能以及降低开发成本。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 位,那么这特别重要。
同一个系统上可以安装多个 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 支持有额外的需求:
HPUX 的需求细节可以在以下网页上找到:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0004877.htm
Linux 的需求细节可以在以下网页上找到:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0004878.htm。关于 Linux 设置的更多信息还可以在以下网页上找到: http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/core/r0012306.htm#wq39(请查看 “Setting up the Linux Java environment” 小节)
防护 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显示了这些映射。
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.htm 和 http://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.htm、http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0009000.htm 和 http://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.javaD:\>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.javaD:\>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.javaD:\>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 上的环境变量 CLASSPATHD:>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=-4301D:\>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.java1 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
本文示例源代码或素材下载
- ››解决Windows 7无法识别杀毒软件的问题
- ››解决无法访问windows installer服务
- ››解决Windows 7网络延迟问题的技巧
- ››解决微软Windows 7系统的四大热门问题
- ››解决Windows 7中无法卸载补丁包更新
- ››解决2000和XP网上邻居互访慢
- ››解决Android平板电脑上开发应用程序不能全屏显示的...
- ››解决ubuntu下mysql不能远程连接数据库的问题
- ››解决android 导入ApiDemos后项目出错
- ››解决VS2008中生成C/C++项目时遇到“生成‘rc.exe’...
- ››解决VisualStudio2008下asp.net mvc开发向View中添...
- ››解决Windows Mobile 唤醒屏不亮的问题之总结
更多精彩
赞助商链接