#调用存储过程
JDBC使用CallableStatement调用数据库中的存储过程和函数。应用程序通过Connection.prepareCall()创建CallableStatement,设置输入参数、注册输出参数,执行调用后再读取返回值。
# 调用方法
JDBC定义了统一的存储过程和函数调用格式。YashanDB JDBC支持以下CALL转义语法:
| 调用对象 | JDBC调用格式 | 说明 |
|---|---|---|
| 存储过程 | {call procedure_name(?, ...)} | 每个?对应一个过程参数。无参数时使用{call procedure_name()}。 |
| 函数 | {? = call function_name(?, ...)} | 第一个?用于接收函数返回值,后续?对应函数参数。 |
CallableStatement常用参数方法如下:
| 参数类型 | 设置或读取方法 | 说明 |
|---|---|---|
| IN | setXxx(parameterIndex, value) | 执行前设置输入值。 |
| OUT | registerOutParameter(parameterIndex, sqlType)、getXxx(parameterIndex) | 执行前注册输出类型,执行后读取输出值。 |
| INOUT | setXxx()、registerOutParameter()、getXxx() | 同一个参数位置既要设置输入值,也要注册并读取输出值。 |
参数索引从1开始。CallableStatement以及由OUT游标返回的ResultSet使用完毕后应及时关闭。存储过程内执行的DML受当前连接的自动提交和事务设置控制。
# 准备示例对象
以下示例使用jdbc_procedure_employee表和5个存储过程。运行Java示例前,使用具有创建表和存储过程权限的用户执行以下SQL:
DROP PROCEDURE IF EXISTS JDBC_PROC_NO_ARGS;
DROP PROCEDURE IF EXISTS JDBC_PROC_ADD_EMPLOYEE;
DROP PROCEDURE IF EXISTS JDBC_PROC_GET_SALARY;
DROP PROCEDURE IF EXISTS JDBC_PROC_DOUBLE_VALUE;
DROP PROCEDURE IF EXISTS JDBC_PROC_LIST_EMPLOYEE;
DROP TABLE IF EXISTS jdbc_procedure_employee;
CREATE TABLE jdbc_procedure_employee (
employee_id INTEGER PRIMARY KEY,
employee_name VARCHAR(50),
department VARCHAR(30),
salary NUMBER(10, 2)
);
INSERT INTO jdbc_procedure_employee
VALUES (1001, 'Alice', 'IT', 8000.00);
INSERT INTO jdbc_procedure_employee
VALUES (1002, 'Bob', 'IT', 9000.00);
CREATE OR REPLACE PROCEDURE JDBC_PROC_NO_ARGS AS
BEGIN
NULL;
END;
/
CREATE OR REPLACE PROCEDURE JDBC_PROC_ADD_EMPLOYEE (
P_ID IN INTEGER,
P_NAME IN VARCHAR,
P_DEPARTMENT IN VARCHAR,
P_SALARY IN NUMBER
) AS
BEGIN
INSERT INTO jdbc_procedure_employee
VALUES (P_ID, P_NAME, P_DEPARTMENT, P_SALARY);
END;
/
CREATE OR REPLACE PROCEDURE JDBC_PROC_GET_SALARY (
P_ID IN INTEGER,
P_SALARY OUT NUMBER
) AS
BEGIN
SELECT salary INTO P_SALARY
FROM jdbc_procedure_employee
WHERE employee_id = P_ID;
END;
/
CREATE OR REPLACE PROCEDURE JDBC_PROC_DOUBLE_VALUE (
P_VALUE IN OUT NUMBER
) AS
BEGIN
P_VALUE := P_VALUE * 2;
END;
/
CREATE OR REPLACE PROCEDURE JDBC_PROC_LIST_EMPLOYEE (
P_DEPARTMENT IN VARCHAR,
P_RESULT OUT SYS_REFCURSOR
) AS
BEGIN
OPEN P_RESULT FOR
SELECT employee_id, employee_name
FROM jdbc_procedure_employee
WHERE department = P_DEPARTMENT
ORDER BY employee_id;
END;
/
# 调用示例
# 调用无参数存储过程
无参数存储过程仍使用一对圆括号。以下示例调用JDBC_PROC_NO_ARGS,execute()正常返回表示过程执行成功。
package quickstart;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
public class NoParameterProcedureExample {
public static void main(String[] args) throws Exception {
String url = "jdbc:yasdb://192.168.1.2:1688/yasdb";
Properties properties = new Properties();
properties.setProperty("user", "username");
properties.setProperty("password", "passwd");
Class.forName("com.yashandb.jdbc.Driver");
try (Connection connection =
DriverManager.getConnection(url, properties);
CallableStatement statement = connection.prepareCall(
"{call JDBC_PROC_NO_ARGS()}")) {
statement.execute();
System.out.println("Procedure executed");
}
}
}
正常情况下,程序输出如下:
Procedure executed
# 调用带IN参数的存储过程
IN参数用于向存储过程传入数据。以下示例使用setInt()、setString()和setBigDecimal()设置4个IN参数,调用过程插入员工,再查询插入结果。
package quickstart;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class InParameterProcedureExample {
public static void main(String[] args) throws Exception {
String url = "jdbc:yasdb://192.168.1.2:1688/yasdb";
Properties properties = new Properties();
properties.setProperty("user", "username");
properties.setProperty("password", "passwd");
Class.forName("com.yashandb.jdbc.Driver");
try (Connection connection =
DriverManager.getConnection(url, properties)) {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate(
"DELETE FROM jdbc_procedure_employee "
+ "WHERE employee_id = 1003");
}
try (CallableStatement statement = connection.prepareCall(
"{call JDBC_PROC_ADD_EMPLOYEE(?, ?, ?, ?)}")) {
statement.setInt(1, 1003);
statement.setString(2, "Carol");
statement.setString(3, "SALES");
statement.setBigDecimal(4, new BigDecimal("7500.00"));
statement.execute();
}
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT employee_name FROM "
+ "jdbc_procedure_employee "
+ "WHERE employee_id = 1003")) {
if (resultSet.next()) {
System.out.println(
"Inserted employee: "
+ resultSet.getString(1));
}
}
}
}
}
正常情况下,程序输出如下:
Inserted employee: Carol
# 调用带OUT参数的存储过程
OUT参数用于从存储过程返回单个值。以下示例在执行前通过registerOutParameter()注册第二个参数为Types.NUMERIC,执行后使用getBigDecimal()读取员工薪资。
package quickstart;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
import java.util.Properties;
public class OutParameterProcedureExample {
public static void main(String[] args) throws Exception {
String url = "jdbc:yasdb://192.168.1.2:1688/yasdb";
Properties properties = new Properties();
properties.setProperty("user", "username");
properties.setProperty("password", "passwd");
Class.forName("com.yashandb.jdbc.Driver");
try (Connection connection =
DriverManager.getConnection(url, properties);
CallableStatement statement = connection.prepareCall(
"{call JDBC_PROC_GET_SALARY(?, ?)}")) {
statement.setInt(1, 1001);
statement.registerOutParameter(2, Types.NUMERIC);
statement.execute();
BigDecimal salary = statement.getBigDecimal(2);
System.out.println("Employee salary: " + salary);
}
}
}
正常情况下,程序输出如下:
Employee salary: 8000
# 调用带INOUT参数的存储过程
INOUT参数在同一位置接收输入值并返回输出值。以下示例先为第一个参数设置4000.00,再将同一参数注册为输出参数;过程执行后返回加倍后的值。
package quickstart;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
import java.util.Properties;
public class InOutParameterProcedureExample {
public static void main(String[] args) throws Exception {
String url = "jdbc:yasdb://192.168.1.2:1688/yasdb";
Properties properties = new Properties();
properties.setProperty("user", "username");
properties.setProperty("password", "passwd");
Class.forName("com.yashandb.jdbc.Driver");
try (Connection connection =
DriverManager.getConnection(url, properties);
CallableStatement statement = connection.prepareCall(
"{call JDBC_PROC_DOUBLE_VALUE(?)}")) {
statement.setBigDecimal(1, new BigDecimal("4000.00"));
statement.registerOutParameter(1, Types.NUMERIC);
statement.execute();
BigDecimal result = statement.getBigDecimal(1);
System.out.println("Result value: " + result);
}
}
}
正常情况下,程序输出如下:
Result value: 8000
# 获取存储过程返回的结果集
YashanDB存储过程可以通过SYS_REFCURSOR类型的OUT参数返回查询结果。JDBC调用时,使用Types.REF_CURSOR注册游标参数,执行后通过该参数的getObject()取得ResultSet。
以下示例将部门名称作为IN参数传入,并读取过程通过第二个OUT参数返回的员工结果集。ResultSet依赖对应的CallableStatement和连接,应在语句及连接关闭前完成读取和关闭。
package quickstart;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.Properties;
public class ResultSetProcedureExample {
public static void main(String[] args) throws Exception {
String url = "jdbc:yasdb://192.168.1.2:1688/yasdb";
Properties properties = new Properties();
properties.setProperty("user", "username");
properties.setProperty("password", "passwd");
Class.forName("com.yashandb.jdbc.Driver");
try (Connection connection =
DriverManager.getConnection(url, properties);
CallableStatement statement = connection.prepareCall(
"{call JDBC_PROC_LIST_EMPLOYEE(?, ?)}")) {
statement.setString(1, "IT");
statement.registerOutParameter(2, Types.REF_CURSOR);
statement.execute();
try (ResultSet resultSet =
(ResultSet) statement.getObject(2)) {
while (resultSet.next()) {
System.out.println(
resultSet.getInt("employee_id") + ", "
+ resultSet.getString(
"employee_name"));
}
}
}
}
}
正常情况下,程序输出如下:
1001, Alice
1002, Bob
Note:
CallableStatement.getResultSet()用于获取当前执行结果中的直接结果集,不用于读取作为OUT参数返回的SYS_REFCURSOR。OUT游标应从对应参数调用getObject()获取。
# 清理示例对象
示例验证完成后,可以执行以下SQL删除测试对象:
DROP PROCEDURE IF EXISTS JDBC_PROC_NO_ARGS;
DROP PROCEDURE IF EXISTS JDBC_PROC_ADD_EMPLOYEE;
DROP PROCEDURE IF EXISTS JDBC_PROC_GET_SALARY;
DROP PROCEDURE IF EXISTS JDBC_PROC_DOUBLE_VALUE;
DROP PROCEDURE IF EXISTS JDBC_PROC_LIST_EMPLOYEE;
DROP TABLE IF EXISTS jdbc_procedure_employee;

