如何用Java读取PL/SQL存储过程返回的字符串输出?
你现在遇到的问题很典型——用普通Statement的executeQuery()去调用带OUT参数的HANA存储过程,自然会报错,因为这个方法只适用于返回结果集的SQL语句(比如SELECT),而你的存储过程是通过OUT参数返回字符串,根本不会生成ResultSet。好消息是完全不用修改存储过程,只要调整Java的调用方式就可以解决。
先回顾下你的存储过程和出错的Java代码:
你的HANA存储过程代码
create PROCEDURE "PRATIK"."PRC_test3" ( IN param1 INT, param2 NVARCHAR(15), OUT outParam1 VARCHAR (2000) ) LANGUAGE SQLSCRIPT SQL SECURITY INVOKER AS BEGIN ----------- ### Exception Handler ### ------------------------ DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN outParam1 := 'SQL Exception occured. Error Code is: ' || ::SQL_ERROR_CODE || ' Error message is: ' || ::SQL_ERROR_MESSAGE; END; outparam1 := ' Param2: ' || :param2 || '| param1: ' || :param1; END;
出错的Java调用代码
if (connection != null) { try { String Stm = "CALL \"PRATIK\".\"PRC_test3_2\"("+param1+",'"+param2+"',"+outparams1+")"; System.out.println("Connection to HANA successful!"); java.sql.Statement stmt = connection.createStatement(); java.sql.ResultSet resultSet = stmt.executeQuery(Stm); resultSet.next(); String res1 = resultSet.getString(1); System.out.println("This is the result of the query -- "+res1); //System.out.println("METADATA -- "+resultSet.getMetaData()); } catch (SQLException e) { System.err.println("Query failed!"); System.err.println(e.getStackTrace()); e.printStackTrace(); } }
报错信息
Query failed! [Ljava.lang.StackTraceElement;@6504e3b2 com.sap.db.jdbc.exceptions.JDBCDriverException: SAP DBTech JDBC: SQL statement does not generate a result set.
at com.sap.db.jdbc.exceptions.SQLExceptionSapDB._newInstance(SQLExceptionSapDB.java:197)
at com.sap.db.jdbc.exceptions.SQLExceptionSapDB.newInstance(SQLExceptionSapDB.java:26)
at com.sap.db.jdbc.StatementSapDB._executeQuery(StatementSapDB.java:1355)
at com.sap.db.jdbc.StatementSapDB.executeQuery(StatementSapDB.java:162)
at com.saphana.tibco.Procedure.main(Procedure.java:33)
解决方案:使用CallableStatement处理OUT参数
要读取存储过程的OUT参数,你需要用JDBC的CallableStatement,而不是普通的Statement。它专门用来处理存储过程的调用,支持注册IN/OUT参数。
修改后的Java代码如下:
if (connection != null) { try { // 使用?作为参数占位符,避免SQL注入,同时正确处理参数类型 String callSql = "{ CALL \"PRATIK\".\"PRC_test3\"(?, ?, ?) }"; java.sql.CallableStatement callStmt = connection.prepareCall(callSql); // 设置IN参数 callStmt.setInt(1, param1); // 第一个参数是param1(INT类型) callStmt.setString(2, param2); // 第二个参数是param2(NVARCHAR类型) // 注册OUT参数:第三个参数是outParam1,类型是VARCHAR(2000),对应JDBC的Types.VARCHAR callStmt.registerOutParameter(3, java.sql.Types.VARCHAR); System.out.println("Connection to HANA successful!"); // 执行存储过程,用execute()而不是executeQuery() callStmt.execute(); // 获取OUT参数的值 String res1 = callStmt.getString(3); System.out.println("This is the output parameter value -- " + res1); // 关闭资源 callStmt.close(); } catch (SQLException e) { System.err.println("Stored procedure call failed!"); e.printStackTrace(); } }
关键修改点说明:
- 使用占位符
?:避免直接拼接SQL导致的SQL注入风险,同时让JDBC正确处理参数类型转换。 - CallableStatement替代Statement:这是处理存储过程的标准方式,支持注册OUT参数。
- registerOutParameter:必须提前告诉JDBC OUT参数的类型,这里你的outParam1是VARCHAR类型,对应
java.sql.Types.VARCHAR。 - 用execute()执行:因为存储过程不返回ResultSet,所以不能用
executeQuery(),改用execute()方法。 - 通过getXXX()获取OUT参数:执行完成后,直接通过
getString(3)获取第三个参数(也就是outParam1)的值,不需要处理ResultSet。
这样修改后,你不用改任何存储过程代码,就能正常读取到OUT参数返回的字符串了。
内容的提问来源于stack exchange,提问作者dhpratik

