SQL Server存储过程被Java调用时无法返回结果集的问题求助
这个问题我之前也碰到过,核心原因是:当你的存储过程里包含INSERT这类DML操作时,SQL Server会默认返回更新计数(也就是受影响的行数),这个结果会排在你需要的SELECT结果集前面。Java的execute()方法执行后,默认指向第一个返回的结果(也就是更新计数),而这并不是ResultSet,所以调用getResultSet()就会抛出"The statement did not return a result set"的异常。
下面给你两种靠谱的修复方案,你可以根据实际情况选择:
方案一:修改存储过程(推荐)
在存储过程的BEGIN块开头添加SET NOCOUNT ON;,这个语句会抑制SQL Server返回DML操作的更新计数,让存储过程只返回你需要的SELECT结果集。
修改后的存储过程代码:
create procedure spAddPerson @ID int, @Name nvarchar(50) as begin SET NOCOUNT ON; -- 关键:关闭更新计数的返回 create table #justTmp ( num int primary key, justName nvarchar(50) ) if @ID in (select C.ContributorID from Contributors C) begin insert into #justTmp values (@ID, @Name) end select * from #justTmp end
修改后,Java代码不需要大改,只需要注意在获取结果集后先判断是否有数据(避免空指针):
CallableStatement cstmt = null; ResultSet rs = null; try { cstmt=conn.prepareCall("{ call spAddPerson(?,?) }"); cstmt.setInt(1, 1); cstmt.setString(2, "MyName"); cstmt.execute(); rs=cstmt.getResultSet(); if (rs.next()) { // 先判断结果集是否有行 int first=rs.getInt(1); String second=rs.getString(2); // 这里处理你的数据逻辑 } } finally { // 务必关闭资源,防止内存泄漏 if (rs != null) rs.close(); if (cstmt != null) cstmt.close(); }
如果你的Java版本是7及以上,更推荐用try-with-resources自动关闭资源,代码更简洁安全:
try (CallableStatement cstmt = conn.prepareCall("{ call spAddPerson(?,?) }")) { cstmt.setInt(1, 1); cstmt.setString(2, "MyName"); cstmt.execute(); try (ResultSet rs = cstmt.getResultSet()) { if (rs.next()) { int first = rs.getInt(1); String second = rs.getString(2); // 处理数据 } } }
方案二:修改Java代码(不修改存储过程的情况)
如果因为权限或其他原因无法修改存储过程,你可以在Java代码中手动跳过所有更新计数,直到找到真正的结果集:
CallableStatement cstmt = null; ResultSet rs = null; try { cstmt = conn.prepareCall("{ call spAddPerson(?,?) }"); cstmt.setInt(1, 1); cstmt.setString(2, "MyName"); boolean hasResultSet = cstmt.execute(); // 跳过所有更新计数,直到找到结果集或者没有更多结果 while (!hasResultSet && cstmt.getUpdateCount() != -1) { hasResultSet = cstmt.getMoreResults(); } if (hasResultSet) { rs = cstmt.getResultSet(); if (rs.next()) { int first = rs.getInt(1); String second = rs.getString(2); // 处理数据 } } } finally { if (rs != null) rs.close(); if (cstmt != null) cstmt.close(); }
这里的逻辑是:execute()返回的boolean表示第一个结果是不是ResultSet。如果是false,说明是更新计数,我们调用getUpdateCount()获取计数(这里可以忽略),然后用getMoreResults()切换到下一个结果,直到找到ResultSet或者没有更多结果(getUpdateCount()返回-1表示没有更多结果)。
内容的提问来源于stack exchange,提问作者Ori Lev

