在一个设计良好的数据库应用中,存储过程(Stored Procedure)几乎是必不可少的。存储过程可以有效的节约查询的网络开销,并且降低应用部署的难度。Java 存储过程是 DB2 存储过程的一个重要分类,既拥有 Java 语言的灵活性又拥有一般存储过程的优点,同时可以让大量的 Java 开发人员只需经过简单的学习,就可以编写服务器端的存储过程应用。
DB2 对 Java 存储过程一直有着良好的支持,DB2 各个版本均自带有 JDK 并且已经配置妥当,用户无需复杂的操作即可直接开始部署 Java 存储过程。并且 DB2 已经提供了整套存储过程部署的工具以方便数据库管理人员。
在 DB2 中 Java 存储过程是一个相对独立的模块,它运行于 JVM 之上而不是直接运行于数据库之中,与用 SQL 写成的存储过程相比,Java 存储过程的编写方法更类似于传统的 Java 应用。下图简单的表示了 DB2 中的执行 Java 存储过程的体系结构:
图 1. DB2 中的执行 Java 存储过程的体系结构
当然,与一般的 Java 程序相比,Java 存储过程在其设计、编写过程中,还是有许多不同之处。本文之后的章节以一个 Java 程序员的角度,从一个简单的例子出发,逐步介绍了如何编写一个 Java 存储过程,并在 DB2 中进行部署及调用该存储过程。
Java 存储过程的编写
在本章节中,我们将通过把一个程序修改为 DB2 中的一个存储过程,来介绍一般存储过程的编写方式。首先我们设计一个简单的数据库查询程序:
清单 1 一个简单的数据库调用函数
public ResultSet hello(int intp) throws ClassNotFoundException,
SQLException {
Class.forName("com.ibm.db2.jcc.DB2Driver");
Connection conn = DriverManager.getConnection(
"jdbc:db2://localhost:50000/test", "user", "password");
String sql = "SELECT * from t1 where c1 = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, intp);
ResultSet rs = ps.executeQuery(sql);
return rs;
}
|
下面一步步将其改写为一个 DB2 标准的 Java 存储过程:
步骤 1:将函数修改为 public static 函数
就像普通 Java 程序中的 main 函数一样,作为入口点的存储过程函数,必须是 public 和 static 的。当数据库用户调用这个存储过程时,数据库会把用户传入的参数进行适当的处理,并传给这个 Java 方法。Java 方法执行完毕之后,将结果返回给数据库。
步骤 2:修改函数返回值的方式
我们知道,数据库中的存储过程是没有函数返回值的概念的,所有的返回值都是通过参数来传递的。在 Java 中并没有 C 语言中的指针的概念,因此想要把返回值放在参数中,就必须要使用数组。从数组中提取返回值的操作由 DB2 完成,我们只需要把需要返回的参数声明为一个数组。具体方法可以参考下面的例子。
经过前两步的修改,我们的程序变为:
清单 2 初步修改的函数
public static void hello(int inp, ResultSet[] rs)
throws ClassNotFoundException, SQLException {
Class.forName("com.ibm.db2.jcc.DB2Driver");
Connection conn = DriverManager.getConnection(
"jdbc:db2://localhost:50000/test", "user", "password");
String sql = "SELECT * from t1 where c1 = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, intp);
rs[0] = ps.executeQuery(sql);
}
|
步骤 3:修改函数中的 connection
这一步是可选的,要依据数据库的需要的具体逻辑来定。一般来说,存储过程中的数据库连接不应该硬编码到程序里,而是获得当前默认的数据库连接——也就是该存储过程安装到的那个数据库。经过这一步的修改,我们的程序变为:
清单 3 可用作存储过程的函数
public static void hello(int inp, ResultSet[] rs)
throws ClassNotFoundException, SQLException {
Class.forName("com.ibm.db2.jcc.DB2Driver");
Connection conn = DriverManager
.getConnection("jdbc:default:connection");
String sql = "SELECT * from t1 where c1 = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, intp);
rs[0] = ps.executeQuery(sql);
}
|
经过这 3 步的修改,我们的 Java 程序已经变成了一个标准的 Java 存储过程程序了。
步骤 4:为存储过程加上更多的返回值(可选)
存储过程可以有多个返回值,并且也可以返回多个结果集,前面我们的示例程序过于简单,还没有覆盖这部分的内容。当然这一步并非必需的,对于许多简单的应用来说,这样的存储过程已经足够了。让我们给这个函数加上更多的返回值,修改后的结果如下:
清单 4 加入了更多返回值的函数
public static void hello(int inp, int[] outp, int[] inoutp,
ResultSet[] rs, ResultSet[] rs2) throws ClassNotFoundException,
SQLException {
Class.forName("com.ibm.db2.jcc.DB2Driver");
Connection conn = DriverManager
.getConnection("jdbc:default:connection");
String sql = "SELECT * from t1 where c1 = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, intp);
rs[0] = ps.executeQuery(sql);
outp[0] = inp * 2;
inoutp[0] = inoutp[0] + 1;
Statement s2 = conn.createStatement();
rs2[0] = s2.executeQuery("SELECT * from t1");
}
|
相应的存储过程中参数的定义为 (IN name VARCHAR(10), IN inp INTEGER, OUT outp INTEGER, INOUT inoutp INTEGER)。
该存储过程返回两个结果集,并且包含一个传出参数和一个可同时传入传出的参数。在 Java 中读取他们的方法在后面章节有介绍。把函数声明和存储过程的参数对照如下表:
表 1 存储过程参数和 Java 函数声明对照表
存储过程参数
示例
Java 参数
示例
IN
IN in INTEGER |
不使用数组 |
int in |
OUT
OUT out INTEGER |
使用数组 |
int[] out |
INOUT
INOUT inout INTEGER |
使用数组 |
int[] inout |
返回结果集
IN in INTEGER |
无需定义 |
ResultSet[] rs |
读者可对照此表,修改自己需要的存储过程定义。
在 DB2 中部署 Java 存储过程
Java 存储过程必须要部署到 DB2 数据库服务器实例的文件系统中,才能被定位、加载和调用执行。完成 Java 存储过程的编写后,使用 JDK 提供的编译器将源程序编译为 Java class 文件,之后便可以将其部署在 DB2 实例上。对于编译后产生多个 Java class 文件的复杂程序,可以使用 JDK 提供的打包工具将其打包到 JAR 文件中以便于部署。打包 JAR 文件的方法与一般 Java 应用相同,并无特殊要求,这里就不再赘述了。需要注意,打包的时候要把我们的工程所依赖的类全部打包进去,以防止将来调用时出现找不到类的异常。部署的过程分成两步:
- 安装 Java class 文件或 JAR 文件到 DB2 数据库服务器实例上;
- 注册 Java 存储过程。
安装 Java class 文件或 JAR 文件到 DB2 数据库服务器实例上
安装 Java class 文件的默认路径是目标 DB2 实例安装路径下的 function 文件夹。通常情况下,它在 Windows 操作系统中位于 SQLLIB\FUNCTION,而 UNIX 操作系统中位于 sqllib/function。确保将 JDK 编译器生成的完整文件结构拷贝到该文件夹中。例如在 Windows 下,com.ibm.sp.TestSP 类生成的 Java class 文件 TestSP.class 应被复制到 SQLLIB\function\com\ibm\sp 下。如果安装 Java class 文件到其他路径,确保被包括在 DB2 实例运行 Java 存储过程的 JVM 的启动变量 CLASSPATH 中,详细情况请参考文献 1。
安装 JAR 文件到目标 DB2 实例下需要在一个 DB2 客户端执行 DB2 内置的存储过程。DB2 提供了一组存储过程进行 JAR 文件的管理,包括安装,替换和卸载。下表列出了它们的名称和用法:
表 2 DB2 管理 JAR 文件的存储过程
名称
功能
用法
sqlj.install_jar
将 JAR 文件安装到 DB2 服务器端 |
CALL sqlj.install_jar( jar-url, jar-id ) |
sqlj.replace_jar
用一个新的 JAR 文件副本替换 DB2 服务器端存在的 JAR 文件 |
CALL sqlj.replace_jar( jar-url, jar-id ) |
sqlj.remove_jar
从 DB2 服务器端删除 JAR 文件 |
CALL sqlj.remove_jar( jar-id ) |
sqlj.refresh_classes
让 DB2 刷新 JAR 文件中包括的所有 class 文件以获得更新 |
CALL sqlj.refresh_classes |
注意:
- jar-url 表示 JAR 文件能够被定位并加载路径的 URL 表示,DB2 只支持’ file: ’一种模式;
- jar-id 是一个最长为 128 个字符的字符串,用来在 DB2 实例上唯一地标识一个 JAR 文件。
如果更新了 class 文件或者 JAR 文件,需要调用 sqlj.refresh_classes 存储过程进行刷新,否则 DB2 实例仍然使用旧副本中的 Java class 文件。
例如,将 TestSP.class 打包到 TestSP.JAR 文件中之后,可以使用 CALL sqlj.install_jar( ' file:///<path>/TestSP.JAR ', 'TESTSP' ) 将其安装在 DB2 实例上;使用 CALL sqlj.replace_jar( ' file:///<path>/TestSP_NEW.JAR ', ' TESTSP ') 和 CALL sqlj.refresh_classes 将 DB2 实例上的 TestSP.JAR 更新为 TestSP_NEW.JAR;使用 CALL sqlj.remove_jar(' TESTSP ') 卸载这个 JAR 文件。
注册 Java 存储过程
完成安装后,使用 CREATE PROCEDURE 语句注册存储过程。CREATE PROCEDURE 语句的参数格式和所有选项信息请参考文献 2,下面是其中几个重要的选项:
- DYNAMIC RESULT SETS:指定存储过程返回几个结果集。存储过程也可能不返回结果集,此时该选项值为 0。
- LANGUAGE:对于 Java 存储过程使用 JAVA。
- EXTERNAL NAME:指定存储过程的 class 文件或 JAR 文件的位置以及文件内的方法。文件的默认位置是目标 DB2 实例的 function 文件夹,也可以指定文件实际位置的完整路径。
- 对于 class 文件,格式为 class_id.method_id,例如 com.ibm.sp.TestSP.hello
- 对于 JAR 文件,格式为 jar-id:class_id.method_id,例如 TESTSP:com.ibm.sp.TestSP.hello
- FENCED / NOT FENCED:指定存储过程是否被认为可以在数据库管理器操作环境的进程或地址空间中“安全地”运行。对于 Java 存储过程一般应指定为 FENCED,只有在确定代码能够安全执行的情况下,才应该使用 NOT FENCED 选项。
- threadsafe / NOT THREADSAFE:指定 FENCED 存储过程的执行是否是线程安全的。仅对 FENCED 存储过程起作用,因为 NOT FENCED 过程默认被指定为 THREADSAFE。
- PARAMETER STYLE:对于 Java 存储过程,只能是 PARAMETER STYLE JAVA 或 PARAMETER STYLE DB2GENERAL。
清单 5 显示一个 CREATE PROCEDURE 语句示例,其中使用了上述选项:
清单 5. CREATE PROCEDURE 语句示例
create procedure db2admin.test_sp_call (in inp integer, out outp integer, inout inoutp)
dynamic result sets 2
language java
external name 'testsp:com.ibm.sp.testsp.hello
fenced
threadsafe
parameter style java;
|
注意: 存储过程的定义务必要与 Java 函数的定义保持一致,否则存储过程会创建失败。
在 Java 中调用存储过程
在 JDBC 中已经提供了调用存储过程的方法,利用 JDBC 就可以方便的调用存储过程。下面代码片段展示了如何 JDBC 中如何调用一个存储过程:
清单 6 通过 JDBC 调用存储过程
String callsp = "call TESPSP (?,?,?)";
CallableStatement callStmt = conn.prepareCall(callsp);
callStmt.setInt(1, 1);
callStmt.registerOutParameter(2, Types.INTEGER);
callStmt.setInt(3, 1);
callStmt.registerOutParameter(3, Types.INTEGER);
callStmt.executeUpdate();
|
该存储过程第一个参数为输入参数,第二个参数为输出参数。注意到我们在 CallableStatement 对象中注册参数的时候,对着两种类型的参数用了两个不同的方法去注册。第三个参数为输入输出参数,则需要同时用两个方法去注册。
在存储过程执行成功后,即可通过 JDBC 提供的方法对结果进行访问。下面的代码片段展示了如何取回该存储过程的两个结果集,以及输出的参数:
清单 7 通过 JDBC 查询存储过程返回结果
ResultSet rs = callStmt.getResultSet();
if (callStmt.getMoreResults()){
ResultSet rs2 = callStmt.getResultSet();
}
int out = callStmt.getInt(2);
|
当存储过程有返回多个结果集时,应该用 getMoreResults() 方法遍历所有的结果集。对 OUT 类型的参数,获得方法是直接用 getXXX() 方法从 CallableStatement 对象中获得。
存储过程开发的注意事项
Java 存储过程与本地运行的 Java 程序运行环境有很大的不同,因此在开发的过程中,有许多独特的问题需要注意。本章节中,笔者会结合以往的一些开发经验和心得,来介绍在 Java 存储过程开发中一些常见问题的解决方案。
线程安全
前面的章节已经介绍过,在部署 Java 存储过程的时候,一个参数是 THREADSAFE,这个参数表明该存储过程是否可以安全的被其他例程所调用。默认情况下 Java 存储过程是使用 THREADSAFE 参数的。因此在我们开发中,要注意通常所说的线程安全性问题。一般来讲,在 Java 程序中就不应该随意使用静态变量,对于各种 I/O 操作也要更加小心。关于线程安全性的问题,在许多 Java 编程的参考书中都有详细介绍,在此我们就不展开论述了。
宿主环境的差异
许多数据库开发人员常常忽视的一个问题,我们在本地进行 Java 存储过程开发,但是这个存储过程最终是要部署到数据库服务器上执行的。执行存储过程的 JVM 是宿主机的 JVM,他上面的配置环境可能会有一些不同。JVM 本身并不是 DB2 的组件之一,对于 JVM 参数的调整,并不一定能够通过调节 DB2 的参数来实现。
例如最常见的多语言问题,在宿主机上返回的时间格式,小数点格式等等,都可能与我们预想的不同。根据笔者的经验,在 JAVA 存储过程的编写过程中,最好能在日志中记录运行时的环境变量,当发生问题时能够比较方便的进行调试。我们可以用如下 Java 代码来获得当前的环境变量:Properties sysProps = System.getProperties()。在出现问题时,这些信息常常是非常有用的。
数据库上存储过程的执行用户
我们这里所讲的执行用户,并非是哪个 DB2 的用户去调用了这个存储过程,而是这个存储过程是在服务器的哪个用户空间之中运行的。举例来说,我们有一个用户 user1 调用了存储过程 TEST_SP_CALL,该存储过程并非是在服务器端 user1 这个用户空间内来执行的(服务器上甚至可能不存在这个用户),真正执行这个存储过程的用户是数据库内建的账号:比如在 Windows 系统上,默认是 db2admin;在 AIX 系统上,默认则是 db2fenc1。
假如存储过程只是进行数据库方面的操作,这个差异并不会造成问题。但是假如在存储过程中还涉及文件存取等涉及系统权限的操作,就一定要注意数据库管理账号是否有相应的权限。同时如果我们希望修改存储过程执行时的某些环境变量,也要在相应的用户环境里面去修改。
JDK 的版本
虽然 DB2 在发布的时候都会带有 JDK,但是用户也可以手工修改来指定他们自己的 JDK 环境。JDK 只能向下兼容,也就是较低版本的 JDK 不能加载更高版本编译的 class 文件。当开发人员需要编写需要部署到许多不同的数据库上的存储过程时,将 Java 的编译级别设为能兼容较低版本的 JDK 是一个比较稳妥的方法。以 Eclipse 集成开发工具为例,修改 Java 的编译兼容级别的方法如下:
在工程上点右键选择 Properties 菜单,之后选择 Java Compiler 选项页。
图 2. Eclipse 中设置 Java 编译选项
此图中笔者将工程的兼容性设置设置为了 JDK1.4,即在 1.4 以上的 JDK 上都可运行。
假如出现了 Java class 版本高于 JDK 版本造成不能加载的情况,可以通过查看 db2diag.log 里面的错误信息来发现。
例子:当用户将 DB2 的 JDK 配置为 1.4,并试图加载一个编译 Java 1.5 环境的存储过程时,可在 db2diag.log 里面看到如下出错信息。
清单 8 db2diag.log
2011-03-24-11.06.26.273703-240 I12591220A1084 LEVEL: Warning
PID : 12903 TID : 12 PROC : db2fmp (Java) 0
INSTANCE: ccintapp NODE : 000
EDUID : 12 EDUNAME: db2fmp (Java) 0
FUNCTION: DB2 UDB, BSU Java support, sqlejLogException, probe:10
DATA #1 : String, 659 bytes
java.lang.UnsupportedClassVersionError: com/ibm/sp/TestSP
(Unsupported major.minor version
49.0)
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:539)
at java.lang.ClassLoader.defineClass(ClassLoader.java:448)
at com.ibm.db2.jcc.uw.classloader.b.a(b.java:236)
at com.ibm.db2.jcc.uw.classloader.a.loadClass(a.java:85)
at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
at com.ibm.db2.jcc.uw.classloader.PowerClassLoader.a(PowerClassLoader.java:71)
at com.ibm.db2.jcc.uw.classloader.a.loadClass(a.java:85)
at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
DATA #2 : Hexdump, 4 bytes
0xFFFFFFFF664FE4AC : 0000 0000
|
JDK 会抛出一个 UnsupportedClassVersionError 错误,在错误说明中出现的 49.0 是 Java 1.5 的内部版本号。
总结和结论
在一个设计良好的数据库应用中,存储过程(Stored Procedure)的使用几乎是必不可少的。使用存储过程可以有效的节约查询的网络开销,并且降低客户端应用部署的难度。对于许多初级的数据库开发人员来说,编写一个拥有复杂逻辑的存储过程是一件非常头疼的事情。Java 存储过程由于其高级语言的灵活性,是刚刚进入数据库领域的开发人员的非常好的选择。对于一个熟练的 Java 开发人员,只需经过简单的学习,就可以编写出完善的服务器端的存储过程应用。本文以一个 Java 程序员的角度,一步步的介绍如何编写出一个完整的数据库存储过程,并将其部署在 DB2 上以及通过 JDBC 的调用的完整步骤。最后本文介绍了一些编写存储过程经常会遇到的问题的解决方案。
分享到:
相关推荐
部署Java存储过程在DB2上有两种主要方法: 1. **使用DB2控制中心或命令行工具**:通过图形界面或SQL命令,可以将编译好的Java类加载到DB2的Java存储区,然后创建存储过程的SQL定义。 2. **利用DB2的API和JDBC**:...
**DB2 Java 存储过程** 主要探讨的是如何在 IBM DB2 数据库环境中利用 Java 编写存储过程。这涉及到 DB2 的一系列产品和服务,特别是与 Java 集成相关的部分。 #### 描述:存储过程的重要性和 DB2 支持概述 存储...
"java 调用db2存储过程" 本文将详细介绍 Java 调用 DB2 存储过程的方法和步骤,并提供了详细的代码示例。 一、创建存储过程 在 DB2 中,创建存储过程需要使用 CREATE PROCEDURE 语句。例如,创建一个无参存储过程...
DB2工具比较少,资料也很少,由于开发过程中用到,也试用了很多工具,开始使用dbeaver.exe工具,用于查询更新数据挺好,...最后发现SqlDbxPersonal挺好用,占用空间少,可以导出完整建表语句,与编写运行DB2存储过程
综上所述,DB2存储过程是提高数据库应用程序效率的重要工具,它们可以简化复杂操作,提高代码重用,减少网络通信,以及提供更好的安全性和控制。熟练掌握DB2存储过程的使用,对于数据库开发者来说是至关重要的。
### DB2存储过程实现分页查询 在数据库应用开发中,分页查询是常见的需求之一。对于大型数据集,直接查询所有数据不仅消耗资源,还会降低用户体验。因此,实现高效、可靠的分页功能至关重要。本篇文章将详细介绍...
- **性能提升**:存储过程在数据库服务器上执行,减少了网络流量,提高了应用程序的整体性能。 - **代码重用**:通过创建可重复使用的存储过程,可以减少代码冗余,简化维护工作。 - **安全性**:可以通过存储过程...
本篇文章将详细讲解如何使用Java来调用DB2存储过程,这对于新手来说是一个很好的起点。 首先,我们需要在Java项目中引入DB2的JDBC驱动。DB2提供了适用于不同操作系统的JDBC驱动,如db2jcc4.jar,确保将其添加到项目...
DB2 存储过程是指在 DB2 服务器端编写、执行的程序单元,可以实现业务逻辑、数据处理和事务控制等功能。存储过程是一种特殊的数据库对象,能够接受输入参数、执行复杂的业务逻辑、返回结果集等。 存储过程的基本...
DB2 存储过程官方教程 DB2 存储过程官方教程是 DB2 存储过程的基础文档,涵盖了基本变量定义、基本语法描述等内容,适合初学者学习和实践。 变量声明是 DB2 存储过程中的一种基本概念,用于声明本地变量,以支持...
### DB2look 导出存储过程脚本 在数据库管理领域,DB2 是 IBM 开发的一款关系型数据库管理系统,广泛应用于各种规模的企业级环境中。为了更好地管理和维护数据库中的对象(如存储过程、触发器等),DB2 提供了一...
为了提高性能并减少网络流量,可以将复杂的数据库操作封装为**存储过程**,并将其部署在数据库服务器上。存储过程可以在服务器端直接执行多条SQL语句,减少网络传输次数,显著提升应用程序的响应速度和整体性能。 #...
DB2存储过程是数据库管理系统DB2中的一个重要特性,它允许用户预先编写一组SQL语句和其他数据库操作,并将它们封装在一起,形成一个可重用的、逻辑上的单元。这个单元可以在需要的时候被调用,提高了代码的复用性和...
然而,在现代版本中,"db2java.jar"可能已经被"db2jcc.jar"所取代,或者可能是一个特定于某个版本或配置的文件。 3. **db2jcc.jar**:这是DB2 JDBC类型4驱动的核心库,它实现了JDBC接口,使得Java应用程序能够通过...
- **性能**:SQL存储过程优于Java存储过程,因为在构建时已经完成了SQL解释、优化和访问计划的创建,且运行在DB引擎上,减少了网络负载。 - **安全性**:使用`GRANT EXECUTE ON PROCEDURE`替代直接的表权限,提高了...
在"DB2存储过程练习"文件中,可能包含了一系列的存储过程示例,如数据的插入、更新、查询等操作,这些实例可以帮助初学者更好地理解如何在实际项目中运用存储过程。 总结,DB2的存储过程是数据库开发的重要工具,...
DB2存储过程是数据库管理系统DB2中的一个重要特性,它允许开发者编写包含一组SQL语句、控制流语句和用户定义函数的程序单元,以便在数据库中执行复杂的逻辑操作。存储过程可以提高应用程序的性能,减少网络流量,...
这两个文件在Java应用程序中起着至关重要的作用,因为它们提供了Java连接DB2数据库所需的驱动程序。以下是关于这些文件以及如何使用它们进行数据库连接的详细解释: 1. `db2jcc.jar`:这个文件是DB2 Universal JDBC...
DB2存储过程是数据库管理中的一个重要概念,它们是预编译的SQL语句集合,可以在数据库服务器上执行复杂的业务逻辑,提高数据处理效率,并提供安全的访问控制。本指南将深入探讨DB2存储过程的开发,帮助你快速掌握这...
* 安全性:DB2 认为 SQL 存储过程是“安全”的,因为它们完全用 SQL 编写,可以直接在 DB Engine 上运行,并且有很好的运行效率和应用范围 存储过程的参数 modes: * IN:传入数据到存储过程 * OUT:存储过程返回...