Java 在SQLServerJDBC中使用表值参数

Java 在SQLServerJDBC中使用表值参数,java,sql-server,jdbc,table-valued-parameters,Java,Sql Server,Jdbc,Table Valued Parameters,有谁能提供一些关于如何在SQLServerJDBC中使用表值参数TVP的指导吗?我使用的是Microsoft提供的SQL Server驱动程序的6.0版本,我已经回顾了 这两个示例都显示了从Connection.prepareStatement调用获取SQLServerPreparedStatement casted对象,以便调用setStructured方法。这难道不会阻止使用标准连接池(如DBCP2)吗 我注意到另一条堆栈溢出注释中的一条注释,该注释表示可以使用stmt.setObject方

有谁能提供一些关于如何在SQLServerJDBC中使用表值参数TVP的指导吗?我使用的是Microsoft提供的SQL Server驱动程序的6.0版本,我已经回顾了

这两个示例都显示了从Connection.prepareStatement调用获取SQLServerPreparedStatement casted对象,以便调用setStructured方法。这难道不会阻止使用标准连接池(如DBCP2)吗

我注意到另一条堆栈溢出注释中的一条注释,该注释表示可以使用stmt.setObject方法:

作为强制转换PreparedStatement的替代方法,您可以将SQLServerDataTable实例传递给PreparedStatement.setObjectint对象方法。这适用于dbo模式中定义的TVP类型阿伦鲁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
任何帮助或指导都将不胜感激。谢谢

re:使用PreparedStatementsetObject

我把它玩弄了一下,也没法让它工作。也许是6.0预览版中的一个功能从最终版本中删除了

re:使用SQLServerPreparedStatement对象

我不明白为什么这会阻止使用DBCP2或任何其他连接池本身,尽管这可能会影响DBCP2的poolPreparedStatements选项,默认情况下该选项为false。尽管如此,如果一个人想要创建一个合适的PreparedStatement对象,并且仍然需要使用setStructured,那么当我刚才尝试它时,它工作得很好:

字符串sql=EXEC dbo.GetAccountsFromTable?; 尝试 连接连接=DriverManager.getConnectionconnectionUrl; PreparedStatement stmt=conn.prepareStatementsql{ SQLServerDataTable帐户=新SQLServerDataTable; accounts.addColumnMetadataitem,java.sql.Types.INTEGER; accounts.addRow123; accounts.addRow234; SQLServerPreparedStatement stmt.setStructured1,dbo.IntegerTable,accounts; try ResultSet rs=stmt.executeQuery{ 而rs.next{ System.out.printlnrs.getInt1; } } }捕获异常e{ e、 printStackTraceSystem.err; }
但是,鉴于我们正在调用一个存储过程,在setStructured调用中使用CallableStatement对象和对SQLServerCallableStatement的快速转换,而不是PreparedStatement对象,这可能是一种很好的形式。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;
    }
}