是否有人可以提供有关如何将表值参数(TVP)与SQL Server JDBC一起使用的一些指导?我使用的是Microsoft提供的6.0版SQL Server驱动程序,我已经查看了official documentation以及更有用的示例
How to pass Table-Valued parameters from java to sql server stored procedure?
这两个示例都显示从SQLServerPreparedStatement
调用中获取Connection.prepareStatement
已转换对象,以便调用setStructured
方法。这是否会阻止使用标准连接池,例如DBCP2?
我注意到其他Stack Overflow评论中的评论说可能会改为使用stmt.setObject
方法:
作为转换PreparedStatement的替代方法,您可以将SQLServerDataTable实例传递给PreparedStatement.setObject(int,Object)方法。这适用于在dbo架构中定义的TVP类型。 - allenru 7月15日19:18
虽然我在尝试此操作时遇到错误,但dbo架构中的类型 ...
Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: The Table-Valued Parameter must have a valid type name.
这是我的代码:
// JAVA
private static void tableParameters(DataSource ds) throws SQLException {
final String sql = "EXEC dbo.GetAccountsFromTable @accountIds=?";
final List<Integer> accountIds = generateIntegers(50, 1_000_000);
try (Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql))
{
SQLServerDataTable accounts = new SQLServerDataTable();
accounts.addColumnMetadata("item", java.sql.Types.INTEGER);
for (Integer aid : accountIds)
accounts.addRow(aid.toString());
stmt.setObject(1, accounts);
try (ResultSet rs = stmt.executeQuery())
{
while (rs.next()) {
System.out.println(rs.getInt(1));
}
}
}
}
-- TSQL
create type dbo.IntegerTable AS TABLE (item INT);
CREATE PROCEDURE dbo.GetAccountsFromTable(@accountIds dbo.IntegerTable READONLY)
AS
BEGIN
IF OBJECT_ID('tempdb..#AccountIds') IS NOT NULL
DROP TABLE #AccountIds
CREATE TABLE #AccountIds (id INTEGER)
INSERT INTO #AccountIds
SELECT * FROM @accountIds
SELECT * FROM #AccountIds
END
感谢任何帮助或指导。谢谢!
答案 0 :(得分:1)
这是我最终使用的路线。 DBCP2有一个DelegatingStatement.getInnermostDelegate
方法来获取Microsoft驱动程序创建的PreparedStatement对象。我不是那种需要跳过的箍的忠实粉丝 - 即使添加了错误检查,代码似乎也很脆弱。 TVP是一个特定于SqlServer的东西,所以使用所需的假设和演员也许并不是那么糟糕。
private static int testTvp(DataSource ds, List<Integer> accountIds) throws SQLException {
final String sql = "EXEC dgTest.GetAccountsFromTvp @accountIds=?";
try (Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
DelegatingPreparedStatement dstmt = (DelegatingPreparedStatement)stmt;
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement)dstmt.getInnermostDelegate();
SQLServerDataTable accounts = new SQLServerDataTable();
accounts.addColumnMetadata("token", java.sql.Types.INTEGER);
for (Integer aid : accountIds)
accounts.addRow(aid);
pstmt.setStructured(1, "dgTest.IntegerTable", accounts);
//// NOTE: The below works for JTDS driver, official MS driver said no result sets were returned
//try (ResultSet rs = pstmt.executeQuery()) {
// return sumInts(rs);
//}
if (pstmt.execute()) {
try (ResultSet rs = pstmt.getResultSet()) {
return sumInts(rs);
}
}
return -1;
}
}
答案 1 :(得分:0)
re:使用PreparedStatement#setObject
我愚弄了一下,我也无法让它工作。也许这是6.0预览中的一个功能从最终版本中拉出来。
re:使用SQLServerPreparedStatement
对象
我不明白为什么会阻止使用DBCP2(或任何其他连接池)本身,尽管它可能会影响DBCP2的poolPreparedStatements
选项({{1} }} 默认情况下)。不过,如果一个人想要创建一个“正确的”false
对象并且仍然需要使用PreparedStatement
,那么当我刚才尝试时这个工作正常:
setStructured
但是,鉴于我们正在调用存储过程,使用String sql = "EXEC dbo.GetAccountsFromTable ?";
try (
Connection conn = DriverManager.getConnection(connectionUrl);
PreparedStatement stmt = conn.prepareStatement(sql)) {
SQLServerDataTable accounts = new SQLServerDataTable();
accounts.addColumnMetadata("item", java.sql.Types.INTEGER);
accounts.addRow(123);
accounts.addRow(234);
((SQLServerPreparedStatement) stmt).setStructured(1, "dbo.IntegerTable", accounts);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getInt(1));
}
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
对象(对CallableStatement
调用快速转换为SQLServerCallableStatement
)可能是“好形式”一个setStructured
对象。