在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中以某种方式执行此操作吗?
答案 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);