将表值参数与SQL Server JDBC

时间:2016-11-02 19:08:36

标签: java sql-server jdbc table-valued-parameters

是否有人可以提供有关如何将表值参数(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

感谢任何帮助或指导。谢谢!

2 个答案:

答案 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对象。