You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SimpleJdbcCall获取存储过程XML输出参数遇异常该如何解决?

解决SQLServerSQLXML调用getString()抛出NegativeArraySize异常的问题

我之前碰到过一模一样的问题——用SimpleJdbcCall调用SQL Server存储过程拿XML输出,结果SQLXML对象调用getString()直接炸NegativeArraySize异常,读二进制流还只拿到开头的ÿþ(这是UTF-16LE的字节序标记)。SSMS跑存储过程完全正常,说明问题出在JDBC驱动对SQLXML参数的解析逻辑上,给你几个实测有效的解决思路:

1. 放弃SimpleJdbcCall,用原生CallableStatement手动处理

SimpleJdbcCall的封装虽然方便,但在处理SQLXML这种特殊类型时容易踩坑。改用原生CallableStatement可以精准控制读取逻辑:

String procSql = "{call dbo.pProcName(?, ?)}";
try (CallableStatement cs = dataSource.getConnection().prepareCall(procSql)) {
    // 设置输入ID参数
    cs.setLong(1, inDTO.getIdCertificado());
    // 重点:把输出参数注册为LONGVARCHAR而不是SQLXML
    cs.registerOutParameter(2, Types.LONGVARCHAR);
    
    cs.execute();
    
    // 直接拿字符串,再手动去掉开头的BOM(如果有的话)
    String xmlContent = cs.getString(2);
    if (xmlContent != null && xmlContent.startsWith("\uFEFF")) {
        xmlContent = xmlContent.substring(1);
    }
    // 现在xmlContent就是正常的XML字符串了
} catch (SQLException e) {
    e.printStackTrace();
}

为什么用Types.LONGVARCHAR?因为SQL Server返回的XML默认是带BOM的UTF-16LE编码,用字符串类型读取可以绕开SQLXML对象的解析bug,直接拿到完整内容。

2. 正确读取SQLXML的二进制流

如果你不想换CallableStatement,那得把SQLXML的二进制流读对——不能只看开头的两个字符,要跳过BOM后用UTF-16LE编码解析:

Map<String, Object> resultMap = simpleCall.execute(params);
SQLXML sqlXml = (SQLXML) resultMap.get("@XmlOut");

try (InputStream xmlStream = sqlXml.getBinaryStream()) {
    // 跳过前2个字节的UTF-16LE BOM(ÿþ对应的就是这两个字节)
    xmlStream.skip(2);
    // 用UTF-16LE编码读取剩余流内容
    InputStreamReader reader = new InputStreamReader(xmlStream, StandardCharsets.UTF_16LE);
    StringBuilder xmlBuilder = new StringBuilder();
    char[] buffer = new char[1024];
    int readLen;
    while ((readLen = reader.read(buffer)) != -1) {
        xmlBuilder.append(buffer, 0, readLen);
    }
    String xmlContent = xmlBuilder.toString();
} catch (IOException | SQLException e) {
    e.printStackTrace();
}

之前你只看到两个字符,是因为没继续读后面的内容,跳过BOM后按正确编码解析就能拿到完整XML了。

3. 升级SQL Server JDBC驱动版本

这个NegativeArraySize异常其实是旧版本mssql-jdbc驱动的已知bug,比如8.x及更早版本容易出现。建议升级到最新的12.x版本,官方修复了不少SQLXML相关的解析问题,说不定升级后直接用getString()就正常了。

4. 调整存储过程的XML输出编码(可选)

如果有权限改存储过程,可以强制让XML输出不带BOM的UTF-8编码:

ALTER PROCEDURE dbo.pProcName
    @ID BIGINT,
    @XmlOut XML OUTPUT
AS
BEGIN
    -- 生成XML时指定TYPE和ROOT,再转换为VARCHAR(MAX)输出
    SET @XmlOut = CAST(
        (SELECT * FROM YourTargetTable WHERE ID = @ID FOR XML PATH('Item'), ELEMENTS, TYPE, ROOT('Root'))
        AS VARCHAR(MAX)
    );
END

不过这种方式要注意XML的大小不能超过VARCHAR(MAX)的限制(大概2GB),如果XML太大还是用前面的方法更稳妥。


内容的提问来源于stack exchange,提问作者H. Grigolli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:20:32