调用存储过程仅指定值为

时间:2017-11-15 16:33:29

标签: 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>>指定参数,因此参数列表包含参数名称 - &gt; 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中以某种方式执行此操作吗?

1 个答案:

答案 0 :(得分:3)

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

  

命名参数可用于仅指定没有默认值的值。

...我们似乎需要为每个可能的参数提供一个参数占位符,并且似乎没有办法为我们可能忽略的参数指定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);