MSSQL多结果集获取问题:Hibernate/JPA调用存储过程无法获取全部结果
解决MSSQL下JPA/Hibernate调用多结果集存储过程的问题
兄弟,我太懂你这个痛点了!用JPA的createStoredProcedureQuery调用MSSQL多结果集存储过程确实踩坑不少——那个带实体类数组的重载完全是为Oracle的REF_CURSOR设计的,MSSQL的结果集返回机制不一样,所以根本没用。而且你测试里遇到的getResultList()返回null、hasMoreResults()一直为false的情况,就是Hibernate官方记录的相关bug导致的,JPA标准API在这块对MSSQL支持不足。
下面给你几个靠谱的解决方法,既能复用现有存储过程,又能拿到所有结果集:
方案一:直接用JDBC原生API(最稳定)
绕开JPA的封装,直接用原生JDBC来处理多结果集,这是最稳妥的方式,完全不受Hibernate/JPA的限制:
public void getMultipleResultSets() { // 从EntityManager获取Hibernate Session,再拿到原生Connection Session session = em.unwrap(Session.class); session.doWork(connection -> { try (CallableStatement cs = connection.prepareCall("{call procWithMultipleResults}")) { boolean hasResults = cs.execute(); int resultSetCount = 0; // 循环遍历所有结果集 while (hasResults) { try (ResultSet rs = cs.getResultSet()) { // 根据结果集索引映射到对应实体 if (resultSetCount == 0) { List<AResult> aResults = mapToAResult(rs); // 处理第一个结果集数据 } else if (resultSetCount == 1) { List<BResult> bResults = mapToBResult(rs); // 处理第二个结果集数据 } resultSetCount++; } // 判断是否还有下一个结果集(要同时判断更新计数,避免漏掉) hasResults = cs.getMoreResults() || cs.getUpdateCount() != -1; } } catch (SQLException e) { throw new RuntimeException("调用存储过程失败", e); } }); } // 手动映射结果集到实体(也可以用BeanUtils、ModelMapper等工具简化) private List<AResult> mapToAResult(ResultSet rs) throws SQLException { List<AResult> list = new ArrayList<>(); while (rs.next()) { AResult result = new AResult(); result.setId(rs.getLong("id")); result.setName(rs.getString("name")); // 其他字段映射 list.add(result); } return list; } private List<BResult> mapToBResult(ResultSet rs) throws SQLException { // 类似上面的映射逻辑 return new ArrayList<>(); }
方案二:使用Hibernate专属的ProcedureCall API
如果你不想完全脱离Hibernate的封装,可以用它的ProcedureCall接口,比JPA的StoredProcedureQuery更灵活,能正确处理MSSQL的多结果集:
public void getMultipleResultsWithHibernate() { Session session = em.unwrap(Session.class); // 创建Hibernate存储过程调用对象 ProcedureCall procedureCall = session.createStoredProcedureCall("procWithMultipleResults"); boolean hasResults = procedureCall.execute(); int resultSetIndex = 0; // 遍历所有输出结果(包括结果集) while (hasResults) { ResultSetOutput resultSetOutput = (ResultSetOutput) procedureCall.getOutputs().getCurrent(); ResultSet rs = resultSetOutput.getResultSet(); // 用Hibernate的NativeQuery把结果集映射到实体 if (resultSetIndex == 0) { List<AResult> aResults = session.createNativeQuery(rs, AResult.class).getResultList(); // 处理第一个结果集 } else if (resultSetIndex == 1) { List<BResult> bResults = session.createNativeQuery(rs, BResult.class).getResultList(); // 处理第二个结果集 } // 移动到下一个输出结果 hasResults = procedureCall.getOutputs().goToNext(); resultSetIndex++; } }
为什么你的测试代码不生效?
你之前写的测试代码里,spq.getResultList()返回null、hasMoreResults()为false,本质是因为Hibernate的JPA实现在MSSQL下,执行完第一个结果集后没有正确维护结果集的遍历状态——JPA标准的StoredProcedureQuery接口并没有针对MSSQL的多结果集做适配,所以必须用上面两种方式绕开。
内容的提问来源于stack exchange,提问作者alltej
相关产品推荐
相关产品推荐

