解决 DB2 UDB Java 存储过程的常见问题
摘自 http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-0510law/
在数据库应用程序中使用存储过程有许多好处,包括减少对网络的使用、提高性能以及降低开发成本。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 有许多配置参数。一些参数在数据库级上定义,其他参数在数据库管理级上定义。影响存储过程行为的大多数参数是在实例级(即数据库管理级)上定义的。
Maximum Number of db2fmps = DB2_FMP_COMM_HEAPSZ / (2*ASLHEAPSZ) |
|
|
需要执行几个步骤,然后才能编译 Java 存储过程。本节讨论设置用于运行 Java 过程的系统所需的步骤。
在开始之前,首先确保数据库服务器上安装了兼容的 JDK/JVM。每种操作系统支持不同的 JDK 级别。如果数据库实例配置为 64 位而不是 32 位,那么这特别重要。
同一个系统上可以安装多个 JVM。为了决定在执行 Java 存储过程时使用哪个 JVM,DB2 读取 JDK_PATH 数据库管理器配置参数。需要确保 JDK_PATH 指向与环境兼容的 JVM。
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 对于 Java 支持有额外的需求:
为了执行 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(或 SQLJ)存储过程。DB2 Application Development Client(如果安装了)包含一组示例,您在初次创建存储过程时可以参考这些示例。Java 存储过程示例位于 sqllib/samples/java/jdbc 目录中,称为 SpServer.java。SQLJ 存储过程示例位于 sqllib/samples/java/sqlj 目录,称为 SpServer.sqlj。
在编写自己的存储过程时需要考虑一些事情。需要决定为存储过程采用哪种参数传递技术。DB2 UDB 对于 Java 应用程序支持两种参数风格:
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 支持自己的一组数据类型。DB2 也有自己的一组数据类型。例如,DB2 数据类型 VARCHAR 在 Java 中不存在。但是,Java 有一个 String
对象,可以替代这种数据类型。DB2 UDB 有一组 “首选的” 数据类型映射,适用于 Java 应用程序和存储过程。表 1(引用于 Infocenter —— 参见 参考资料)显示了这些映射。
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
代码转换成 java
代码,并且创建一个 SQLJ 可序列化(.ser)文件。 db2sqljcustomize
命令。 关于 SQLJ 的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/ad/t0007588.htm
如果使用 JAR 文件而不是类文件,就需要采取一些额外的步骤,使 DB2 认识到 JAR 文件是存储过程类的容器。DB2 附带四个内置的存储过程,帮助管理 JAR 文件。
CALL sqlj.install_jar( jar-url, jar-id )
CALL sqlj.replace_jar( jar-url, jar-id )
CALL sqlj.remove_jar( jar-id )
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
语句中,可以为存储过程指定几个选项。下面是其中几个重要的选项:
‘jar-id!class_id.method_id’
或 ‘class_id.method_id’
。 关于 CREATE PROCEDURE
语句的更多信息,请参阅:http://publib.boulder.ibm.com/infocenter/db2help/topic/com.ibm.db2.udb.doc/admin/r0008328.htm
清单 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。
清单 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 错误消息提供例子,但是这种错误也值得注意。前面在讨论为 Java 存储过程支持设置环境时提到过,Linux 和 HPUX 平台需要额外的步骤。如果没有执行这些额外步骤,就会发生 SQL4301 RC=2 错误。如果您使用这些平台,那么请确保正确地设置了环境。
清单 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. |
清单 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 常常意味着在 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 |
清单 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 |
清单 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 |