sqljdbc4与mssql-jdbc中Statement.execute()行为差异及问题咨询
JDBC驱动切换后存储过程结果集获取异常问题
问题背景
我们的Java代码使用Statement执行SQL的逻辑如下:
Statement stmt = _CONN.createStatement(); boolean isRst = stmt.execute(sql); if (isRst) { do { ResultSet rs = stmt.getResultSet(); setColumnInfo(rs); while (rs.next()) outputOneRecord(rs); rs.close(); } while (fullerror && stmt.getMoreResults()); } else { int r = stmt.getUpdateCount(); if (r != -1) logger.info("Update count: {}", r); }
该逻辑已稳定运行多年。近期将JDBC驱动依赖从旧版本:
<dependency> <groupId>com.microsoft</groupId> <artifactId>sqljdbc4</artifactId> <version>4.0.2206.100</version> </dependency>
切换为新版本:
<dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>mssql-jdbc</artifactId> <version>12.4.1.jre11</version> </dependency>
切换后,多数功能正常,但一个先执行更新操作、最终以SELECT语句结尾的存储过程出现异常:旧驱动会返回SELECT语句的结果,新驱动下stmt.execute(sql)返回false,代码仅上报更新计数后退出,未输出任何行数据。仅切换回旧驱动即可恢复正常。
问题解答
这是已文档化的行为变更,并非新驱动的Bug。
原因说明
旧的sqljdbc4驱动行为不符合JDBC规范:JDBC规范明确规定,Statement.execute()方法的返回值表示第一个结果的类型——如果第一个结果是ResultSet则返回true,如果是更新计数则返回false。但旧驱动在处理多结果语句(如包含更新+查询的存储过程)时,错误地将最后一个结果的类型作为返回值,导致即使第一个结果是更新操作,也会返回true。
新的mssql-jdbc驱动修正了这个问题,严格遵循JDBC规范,返回第一个结果的类型。因此当存储过程第一个操作是更新时,execute()返回false,原代码仅处理一次更新计数就退出,漏掉了后续的SELECT结果集。
解决方案
修改代码逻辑,不再依赖execute()的初始返回值,而是循环遍历所有结果,直到没有更多结果为止。正确的处理逻辑示例:
Statement stmt = _CONN.createStatement(); boolean hasResults = stmt.execute(sql); do { if (hasResults) { // 处理结果集 ResultSet rs = stmt.getResultSet(); setColumnInfo(rs); while (rs.next()) outputOneRecord(rs); rs.close(); } else { // 处理更新计数 int updateCount = stmt.getUpdateCount(); if (updateCount != -1) { logger.info("Update count: {}", updateCount); } } // 切换到下一个结果 hasResults = stmt.getMoreResults(); } while (hasResults || stmt.getUpdateCount() != -1); stmt.close();
这段代码会依次处理所有的更新计数和结果集,无论它们出现的顺序如何,完全符合JDBC规范,能兼容新旧驱动。
内容的提问来源于stack exchange,提问作者Mikhail T.
相关产品推荐
相关产品推荐

