Java Database Connectivity (JDBC) API 是 J2EE 的一部分,是 Java 语言访问关系数据库的基于标准的首要机制,提供了对数据库访问和缓存管理的直接控制。
JDBC 中的 CallableStatement 对象为所有的关系数据库管理系统 (RDBMS: Relational Database Management System) 提供了一种标准形式调用存储过程的方法。对存储过程的调用有两种形式:带结果参数和不带结果参数。结果参数是一种输出参数,是存储过程的返回值。两种形式都可带有数量可变的输入(IN 参数)、输出(OUT 参数)或输入和输出(INOUT 参数)的参数。
在 JDBC 中调用存储过程的语法为:{call procedure_name[(?, ?, ...)]};返回结果参数的存储过程的语法为:{? = call procedure_name[(?, ?, ...)]};不带参数的存储过程的语法为:{call procedure_name}。其中,问号代表参数,方括号表示其间的内容是可选项。
使用 CallableStatement 对象调用存储过程的过程如下:
- 使用 Connection.prepareCall 方法创建一个 CallableStatement 对象。
- 使用 CallableStatement.setXXX 方法给输入参数(IN)赋值。
- 使用 CallableStatement.registerOutParameter 方法来指明哪些参数只做输出参数(OUT),哪些是输入输出参数(INOUT)。
- 调用以下方法之一来调用存储过程:
- int CallableStatement.executeUpdate: 存储过程不返回结果集。
- ResultSet CallableStatement.executeQuery: 存储过程返回一个结果集。
- Boolean CallableStatement.execute: 存储过程返回多个结果集。
- int[] CallableStatement.executeBatch: 提交批处理命令到数据库执行。
- 如果存储过程返回结果集,则得到其结果集。
- 调用 CallableStatement.getXXX 方法从输出参数 (OUT) 或者输入输出参数 (INOUT) 取值。
- 使用完 CallableStatement 对象后,使用 CallableStatement.close 方法关闭 CallableStatement 对象。
清单1是一个使用 executeUpdate 来执行的存储过程的例子。存储过程名为 exampleJDBC,含有五个参数,前两个分别是 String 和 Int 类型的输入参数,后三个分别是整型,整型和字符型的输出参数。分别给输入参数赋值 ”Beijing” 和 2008,执行 executeUpdate 命令后,从后三个输出参数中可以得到输出值,没有结果集返回。需要说明的是,这里的参数下标是以 1 开始的,与 java 数组下标以 0 开始不同。
清单 1. 使用 executeUpdate 来执行的存储过程
Connection con = null; ... // Create a CallableStatement object CallableStatement cstmt = con.prepareCall("CALL exampleJDBC (?, ?, ?, ?, ?)"); cstmt.setString (1, “BeiJing”); // Set input parameter cstmt.setInt (2, 2008); // Set input parameter cstmt.registerOutParameter (3, Types.INTEGER); cstmt.registerOutParameter (4, Types.INTEGER); cstmt.registerOutParameter (5, Types.VARCHAR); cstmt.executeUpdate(); // Call the stored procedure int goldnumber = cstmt.getInt(3); // Get the output parameter values int silvernumber = cstmt.getInt(4); String errorinfo = cstmt.getString(5); cstmt.close(); |
当存储过程返回一个结果集时,只需遍历该结果集便可以得到存储过程执行的所有结果。具体例子见清单2。
清单 2. 存储过程返回一个结果集
CallableStatement cstmt = null; … boolean moreResultSets = cstmt.execute(); ResultSet rs1 = cstmt.getResultSet(); while (rs1.next()) System.out.println(rs1.getString(1) + " " + rs1.getString(2)); |
当存储过程返回多个结果集时,遍历所有结果集才能得到执行的所有结果,使用 getMoreResults() 方法跳转到下一个结果集。具体例子见清单3。
清单 3. 存储过程返回多个结果集
CallableStatement cstmt = null; … While (cstmt.getMoreResults()) { ResultSet rs2 = cstmt.getResultSet(); while (rs2.next()) System.out.println(rs2.getString(1) + " " + rs2.getString(2)); rs2.close(); } |
如果存储过程返回多个结果集,每个结果集的数据结构都不一样,或者某些结果集的数据结构未知,则可以使用 getColumnName() 方法来得到结果集中数据的列名。具体例子见清单 4。
清单 4. 存储过程返回多个结果集,并且每个结果集的数据结构未知或者不一样
CallableStatement cstmt = null; … boolean moreResultSets = cstmt.execute(); while (moreResultSets) { ResultSet rs = cstmt.getResultSet(); ResultSetMetaData rsmd = rs.getMetaData(); StringBuffer buffer = new StringBuffer(); for (int i = 1; i <= rsmd.getColumnCount(); i++){ buffer.append(rsmd.getColumnName(i)).append("/t"); System.out.println(buffer.toString()); while (rs.next()) { buffer.setLength(0); for (int i = 1; i <= rsmd.getColumnCount(); i++) buffer.append(rs.getString(i)).append("/t"); System.out.println(buffer.toString()); } } rs.close(); moreResultSets = cstmt.getMoreResults(); } |
多年来 JDBC 一直是 Java 开发人员进行数据访问的标准,这是一种稳定且被广泛证实的技术,目前已经发展成可以提供完全具有高速缓存和资源池机制的完善的数据库驱动程序。使用 JDBC 来调用存储过程是最常见的一种方式,由于 JDBC 是最接近于数据库的 API,因而其效率也是最高的。CallableStatement 对象为所有的 DBMS 提供了标准形式调用存储过程的方法,对于要求实现灵活,执行效率要求比较高应用,直接采用 JDBC API 来实现存储过程能很好地满足需要。
JDBC API 是 J2EE 定义的访问后端数据库的标准 API,在 JDBC 的基础上,IBM WebSphere 部门从 WebSphere Application Server 5.0 开始提供了一个扩展的数据库访问框架 IBM DB Beans。这个框架提供了 JDBC 所不具有的众多扩展功能,包括带参数的查询、跨越多个事务的结果集缓冲以及通过缓冲执行更新、元数据映射等等,同时它也隐藏了用 JDBC 访问后端数据库时必须涉及的许多复杂细节,被设计成支持任何支持 JDBC 的数据源,使得 Bean与各种 DBMS 无关。
WebSphere 的数据库访问扩展框架的 API 可以在 IBM 基于 Eclipse 的各种集成开发工具的 plugins 目录下找到,通常为 plugins 目录下插件 com.ibm.datatools.dbjars 所在目录下的 dbbeans.jar 文件中,开发时只要把这个 jar 文件放入项目的编译路径就可以使用。
在 IBM 扩展的数据库访问框架中,DBProcedureCall 对象用于调用后端数据库的存储过程,并管理存储过程的执行结果。调用 DBStatement.setConnectionSpec() 可以设置 SQL 命令的连接属性,由于这一机制,同一个连接属性设置可用于一个以上的 SQL 命令。
DBProcedureCall 继承自 DBStatement,通过 DBProcedureCall 调用存储过程时,获取元数据使用 DBStatement 的 getParameterMetaData() 方法。参数可以是自定义的 Java 类,也可以是 Java 的标准数据类型;如果要将 Java 数据类型映射到数据库字段类型,这一机制是非常有用的。