Java调用Oracle存储过程详解
在Java应用程序中调用Oracle存储过程是一个常见的任务,下面我将详细介绍几种常用的方法。

基本概念
Oracle存储过程是存储在数据库中的一段PL/SQL代码,可以通过Java程序调用并传递参数,Java调用存储过程主要通过JDBC(Java Database Connectivity)实现。
调用无参数的存储过程
import java.sql.*;
public class CallStoredProcedureExample {
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
String user = "username";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 创建CallableStatement对象
CallableStatement cstmt = conn.prepareCall("{call procedure_name()}");
// 执行存储过程
cstmt.execute();
System.out.println("存储过程执行成功");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
调用带输入参数的存储过程
// 假设存储过程定义为: PROCEDURE add_emp (emp_id IN NUMBER, emp_name IN VARCHAR2)
try (Connection conn = DriverManager.getConnection(url, user, password)) {
CallableStatement cstmt = conn.prepareCall("{call add_emp(?, ?)}");
// 设置输入参数
cstmt.setInt(1, 1001); // 第一个参数
cstmt.setString(2, "John Doe"); // 第二个参数
cstmt.execute();
System.out.println("添加员工成功");
} catch (SQLException e) {
e.printStackTrace();
}
调用带输出参数的存储过程
// 假设存储过程定义为: PROCEDURE get_emp_name (emp_id IN NUMBER, emp_name OUT VARCHAR2)
try (Connection conn = DriverManager.getConnection(url, user, password)) {
CallableStatement cstmt = conn.prepareCall("{call get_emp_name(?, ?)}");
// 设置输入参数
cstmt.setInt(1, 1001);
// 注册输出参数
cstmt.registerOutParameter(2, Types.VARCHAR);
cstmt.execute();
// 获取输出参数
String empName = cstmt.getString(2);
System.out.println("员工姓名: " + empName);
} catch (SQLException e) {
e.printStackTrace();
}
调用带输入输出参数的存储过程
// 假设存储过程定义为: PROCEDURE update_salary (emp_id IN NUMBER, new_salary IN OUT NUMBER)
try (Connection conn = DriverManager.getConnection(url, user, password)) {
CallableStatement cstmt = conn.prepareCall("{call update_salary(?, ?)}");
// 设置输入参数
cstmt.setInt(1, 1001);
cstmt.setDouble(2, 5000.00); // 初始值
// 注册输入输出参数
cstmt.registerOutParameter(2, Types.NUMERIC);
cstmt.execute();
// 获取修改后的值
double updatedSalary = cstmt.getDouble(2);
System.out.println("更新后的薪资: " + updatedSalary);
} catch (SQLException e) {
e.printStackTrace();
}
调用返回结果集的存储过程
Oracle存储过程可以通过REF CURSOR返回结果集,Java中需要使用Oracle特定的类型来处理:
import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleTypes;
// 假设存储过程定义为: PROCEDURE get_emps_by_dept (dept_id IN NUMBER, emp_cursor OUT SYS_REFCURSOR)
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 需要转换为OracleConnection才能创建OracleCallableStatement
OracleConnection oraConn = conn.unwrap(OracleConnection.class);
CallableStatement cstmt = oraConn.prepareCall("{call get_emps_by_dept(?, ?)}");
// 设置输入参数
cstmt.setInt(1, 10);
// 注册输出参数为REF CURSOR
cstmt.registerOutParameter(2, OracleTypes.CURSOR);
cstmt.execute();
// 获取结果集
ResultSet rs = ((OracleCallableStatement)cstmt).getCursor(2);
// 处理结果集
while (rs.next()) {
System.out.println("ID: " + rs.getInt("emp_id") +
", Name: " + rs.getString("emp_name"));
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
最佳实践
- 使用try-with-resources:确保Connection、Statement和ResultSet等资源被正确关闭
- 参数化查询:始终使用参数化查询而非字符串拼接,防止SQL注入
- 错误处理:妥善处理SQLException
- 连接池:在生产环境中使用连接池(如HikariCP)提高性能
- 批处理:对于大量调用,考虑使用批处理提高效率
完整示例
import java.sql.*;
import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleTypes;
public class OracleStoredProcedureExample {
public static void main(String[] args) {
String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
String user = "username";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 示例1: 调用无参数存储过程
callSimpleProcedure(conn);
// 示例2: 调用带输入参数的存储过程
callProcedureWithInput(conn, 1001, "John Doe");
// 示例3: 调用带输出参数的存储过程
callProcedureWithOutput(conn, 1001);
// 示例4: 调用返回结果集的存储过程
callProcedureWithResultSet(conn, 10);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 无参数存储过程
private static void callSimpleProcedure(Connection conn) throws SQLException {
try (CallableStatement cstmt = conn.prepareCall("{call simple_procedure()}")) {
cstmt.execute();
System.out.println("简单存储过程执行成功");
}
}
// 带输入参数的存储过程
private static void callProcedureWithInput(Connection conn, int empId, String empName) throws SQLException {
try (CallableStatement cstmt = conn.prepareCall("{call add_emp(?, ?)}")) {
cstmt.setInt(1, empId);
cstmt.setString(2, empName);
cstmt.execute();
System.out.println("添加员工成功");
}
}
// 带输出参数的存储过程
private static void callProcedureWithOutput(Connection conn, int empId) throws SQLException {
try (CallableStatement cstmt = conn.prepareCall("{call get_emp_name(?, ?)}")) {
cstmt.setInt(1, empId);
cstmt.registerOutParameter(2, Types.VARCHAR);
cstmt.execute();
String empName = cstmt.getString(2);
System.out.println("员工ID: " + empId + ", 姓名: " + empName);
}
}
// 返回结果集的存储过程
private static void callProcedureWithResultSet(Connection conn, int deptId) throws SQLException {
OracleConnection oraConn = conn.unwrap(OracleConnection.class);
try (CallableStatement cstmt = oraConn.prepareCall("{call get_emps_by_dept(?, ?)}")) {
cstmt.setInt(1, deptId);
cstmt.registerOutParameter(2, OracleTypes.CURSOR);
cstmt.execute();
try (ResultSet rs = ((OracleCallableStatement)cstmt).getCursor(2)) {
System.out.println("部门 " + deptId + " 的员工:");
while (rs.next()) {
System.out.println("ID: " + rs.getInt("emp_id") +
", Name: " + rs.getString("emp_name"));
}
}
}
}
}
通过以上示例,你应该能够掌握在Java中调用Oracle存储过程的各种方法,根据你的具体需求选择合适的方式实现。

