sql-server - 调用存储过程,仅指定带值的参数

标签 sql-server stored-procedures jdbc parameters mssql-jdbc

在 SQL Server 2016 的一个实例中,我有一个包含几十个参数的存储过程。例如:

CREATE PROCEDURE spName (
    @par1 INT = NULL,
    @par2 VARCHAR(10) = NULL,
        ....
        ....
    @par98 INT = NULL,
    @par99 INT = NULL,
) AS
BEGIN
    ....
    ....
END

我有一个用 C# 编写的客户端,它调用存储过程,只指定带有值的参数。前任:
SqlCommand cmd = new SqlCommand();

cmd.CommandText = "spName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = dbConn;

cmd.Parameters.Add(new SqlParameter("par1", "val1"));
cmd.Parameters.Add(new SqlParameter("par47", "val47"));

...

cmd.ExecuteNonQuery();

它完美地工作!因此,该过程被执行并且只有 2 个参数(par1 和 par47)有一个值。其他参数保持默认值 (NULL)。

我会从使用 Microsoft JDBC 驱动程序 6.2 的 Java 客户端执行相同的操作。
我用 List<Map<String, Object>> 指定参数,所以是一对parameterName-->parameterValue的列表。以下方法构建 PreparedStatement目的:
private CallableStatement prepareStatement(String spName, Map<String, ?> parameters) throws SQLException {
    setupConnection();
    CallableStatement stmt = null;
    try {
        stmt = conn.prepareCall(getSpCallString(spName, parameters));
        if (parameters != null) {
            for (String parName : parameters.keySet())
                stmt.setObject(parName, parameters.get(parName));
        }
    } catch (SQLException e) {
        ApplicationLogging.severe("Cannot prepare callable statement", e);
        throw e;
    }
    return stmt;
}

getSpCallString() 方法生成 { call spName ?,?, ... , ? } 类型的字符串。数量为?作为传递给过程的带有值的参数的数量,所以不是所有的 99 个参数。如果我有 2 个参数,它会生成字符串 { call spName ?,? } .
例如,通过传递 par15=val15 和 par47=val47 会引发以下异常:
com.microsoft.sqlserver.jdbc.SQLServerException: The index 2 is out of range.

我可以解决这个问题,在调用命令中输入相同数量的 ?作为存储过程的参数数量,但是......我不知道每个存储过程的参数数量(及其位置)!
在 C# 中,这很容易解决,因为参数只分配了它们的名称,因此参数的数量和顺序实际上是一个黑盒子。

我可以在 Java 中以某种方式做到这一点吗?

最佳答案

这是 confirmed deficiency在对 CallableStatement 的命名参数支持的当前实现中在 mssql-jdbc 驱动程序中。尽管 JDBC 4.2 规范的第 13.3.2 节声明...

Named parameters can be used to specify only the values that have no default value.



...我们似乎需要为每个可能的参数提供一个参数占位符,而且似乎没有办法指定 DEFAULT对于我们可能会简单地省略的参数。

作为一种解决方法,我们可以使用这样的代码

public static ResultSet executeStoredProcedureQuery(
        Connection conn, String spName, Map<String, Object> paramItems) 
        throws SQLException {
    StringBuffer sqlBuf = new StringBuffer("EXEC ");
    sqlBuf.append(spName);
    int paramCount = 1;
    for (String paramName : paramItems.keySet()) {
        sqlBuf.append(
                (paramCount++ > 1 ? ", " : " ") + 
                (paramName.startsWith("@") ? "" : "@") + paramName + "=?");
    }
    String sql = sqlBuf.toString();
    myLogger.log(Level.INFO, sql);
    // e.g., EXEC dbo.BreakfastSP @helpings=?, @person=?, @food=?
    PreparedStatement ps = conn.prepareStatement(sql);
    paramCount = 1;
    for (String paramName : paramItems.keySet()) {
        ps.setObject(paramCount++, paramItems.get(paramName));
    }
    return ps.executeQuery();
}

我们可以这样称呼
// test data
Map<String, Object> paramItems = new HashMap<>();
paramItems.put("@person", "Gord");
paramItems.put("@food", "bacon");
paramItems.put("@helpings", 3);
//
ResultSet rs = executeStoredProcedureQuery(conn, "dbo.BreakfastSP", paramItems);

关于sql-server - 调用存储过程,仅指定带值的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47312673/

相关文章:

java - 数据库池 - 在 Spring3 MVC 中连接到 Mysql 数据库

mysql - JDBC 连接- Class.forName 与 Class.forName().newInstance?

c# - 替换列中的多个字符串元素

sql - Powershell和SQL:如何将查询的值存储在变量中?

Mysql分步向导失败

sql - 从嵌套表中获取数据到游标中

java - 如何使用 Apache DbUtils 调用 SQL 标量函数

sql - 带日期时间的 MSSQL 转换不输出 Z 时区指示符

sql-server - 如何在批处理文件中获取SQL Server脚本错误

SQL Server : how to get specific column from stored procedure without modifying it