Utilisation de parameters de table avec SQL Server JDBC

Quelqu'un pourrait-il fournir des conseils sur la façon d'utiliser les parameters de valeur de table (TVP) avec SQL Server JDBC? J'utilise la version 6.0 du pilote SQL Server fourni par Microsoft, et j'ai examiné la documentation officielle ainsi que l'exemple plus utile

Comment passer les parameters Table-Valued de la procédure stockée du server Java vers SQL?

Les deux exemples montrent l'obtention d'un object cast SQLServerPreparedStatement partir d'un appel Connection.prepareStatement afin d'appeler la méthode setStructured . Cela n'empêcherait-il pas l'utilisation de pools de connections standard tels que DBCP2?

J'ai remarqué un commentaire de l'autre commentaire Stack Overflow disant qu'il pourrait être possible d'utiliser la méthode stmt.setObject place:

Au lieu de lancer PreparedStatement, vous pouvez passer l'instance SQLServerDataTable à la méthode PreparedStatement.setObject (int, Object). Cela a fonctionné pour un type TVP qui a été défini dans le schéma dbo. – allenru le 15 juillet à 19:18

Bien que j'ai eu une erreur quand j'ai essayé ceci, et le type est dans le schéma dbo …

 Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: The Table-Valued Parameter must have a valid type name. 

Voici mon code:

 // JAVA private static void tableParameters(DataSource ds) throws SQLException { final Ssortingng 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.toSsortingng()); 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 

Toute aide ou orientation est appréciée. Merci!

Voici la route que j'ai fini par utiliser. DBCP2 a une méthode DelegatingStatement.getInnermostDelegate pour get l'object PreparedStatement créé par le pilote Microsoft. Je ne suis pas un grand fan des cerceaux nécessaires pour sauter à travers – même si la vérification des erreurs a été ajouté le code semble fragile. TVP est une chose spécifique à SqlServer, alors peut-être que ce n'est pas si mal d'utiliser les hypothèses et les dissortingbutions requirejses.

 private static int testTvp(DataSource ds, List<Integer> accountIds) throws SQLException { final Ssortingng 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; } } 

re: using PreparedStatement#setObject

Je me suis un peu amusé avec ça et je ne pouvais pas le faire fonctionner non plus. Peut-être que c'était une fonctionnalité dans l'aperçu 6.0 qui a été tiré de la version finale.

re: à l'aide d'un object SQLServerPreparedStatement

Je ne vois pas pourquoi cela empêcherait d'utiliser DBCP2 (ou tout autre pool de connections) en soi , bien que cela puisse avoir des implications pour l'option poolPreparedStatements de poolPreparedStatements (qui est false par défaut). Pourtant, si l'on voulait créer un object PreparedStatement "correct" et qu'il fallait encore utiliser setStructured alors cela a bien fonctionné quand je l'ai essayé tout de suite:

 Ssortingng 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); } 

Cependant, étant donné que nous appelons une procédure stockée, il peut être "bon" d'utiliser un object CallableStatement (avec un prompt cast à SQLServerCallableStatement pour l'appel setStructured ) au lieu d'un object PreparedStatement .