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 对象调用存储过程的过程如下:
1. 使用 Connection.prepareCall 方法创建一个 CallableStatement 对象。
2. 使用 CallableStatement.setXXX 方法给输入参数(IN)赋值。
3. 使用 CallableStatement.registerOutParameter 方法来指明哪些参数只做输出参数(OUT),哪些是输入输出参数(INOUT)。
4. 调用以下方法之一来调用存储过程:
* int CallableStatement.executeUpdate: 存储过程不返回结果集。
* ResultSet CallableStatement.executeQuery: 存储过程返回一个结果集。
* Boolean CallableStatement.execute: 存储过程返回多个结果集。
* int[] CallableStatement.executeBatch: 提交批处理命令到数据库执行。
5. 如果存储过程返回结果集,则得到其结果集。
6. 调用 CallableStatement.getXXX 方法从输出参数 (OUT) 或者输入输出参数 (INOUT) 取值。
7. 使用完 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 来实现存储过程能很好地满足需要。