使用 JDBC 将用户定义的表类型传递给 SQL Server 存储过程

Posted

技术标签:

【中文标题】使用 JDBC 将用户定义的表类型传递给 SQL Server 存储过程【英文标题】:Passing a user-defined table type to a SQL Server stored procedure using JDBC 【发布时间】:2019-09-24 22:51:35 【问题描述】:

我们在 SQL Server 中有这种用户定义的表类型:

CREATE TYPE [dbo].[INITVALS_MSG] AS TABLE(
    [SDate] [decimal](8, 0) NOT NULL,
    [EDate] [decimal](8, 0) NOT NULL,
    [PlantCode] [nvarchar](10) NOT NULL,
    [LoadType] [nchar](8) NOT NULL,
    [Asset] [bigint] NOT NULL
)

以及以该表为输入的存储过程:

ALTER PROCEDURE [dbo].[RegisterInitAssets]
    @initmsg INITVALS_MSG ReadOnly
AS
BEGIN
    ...

现在,我需要从 java 调用这个过程。有可能做这样的事情吗? JDBC 支持吗?

--------编辑 我在 java 中有一个对应于该类型的类:

public class DBInitialAsset 
    private Integer sDate;
    private Integer eDate;
    private String plantCode;
    private String loadType;
    private Integer asset;

    public DBInitialAsset() 
    

【问题讨论】:

【参考方案1】:

是的,现在可以了。 Microsoft 的 SQL Server JDBC 驱动程序 6.0 版增加了对表值参数的支持。

以下代码示例显示了如何

使用SQLServerDataTable对象来保存要传递的表数据,并且 调用SQLServerCallableStatement#setStructured 方法将该表传递给存储过程。
SQLServerDataTable sourceDataTable = new SQLServerDataTable();   
sourceDataTable.addColumnMetadata("SDate", java.sql.Types.DECIMAL);
sourceDataTable.addColumnMetadata("EDate", java.sql.Types.DECIMAL);
sourceDataTable.addColumnMetadata("PlantCode", java.sql.Types.NVARCHAR);
sourceDataTable.addColumnMetadata("LoadType", java.sql.Types.NCHAR);
sourceDataTable.addColumnMetadata("Asset", java.sql.Types.BIGINT);

// sample data
sourceDataTable.addRow(123, 234, "Plant1", "Type1", 123234);   
sourceDataTable.addRow(456, 789, "Plant2", "Type2", 456789);   

try (CallableStatement cs = conn.prepareCall("CALL dbo.RegisterInitAssets (?)")) 
    ((SQLServerCallableStatement) cs).setStructured(1, "dbo.INITVALS_MSG", sourceDataTable);
    boolean resultSetReturned = cs.execute();
    if (resultSetReturned) 
        try (ResultSet rs = cs.getResultSet()) 
            rs.next();
            System.out.println(rs.getInt(1));
        
    

有关更多详细信息,请参阅以下 MSDN 文章:

Using Table-Valued Parameters

【讨论】:

3 岁的问题!我当时使用xml参数来解决这个问题。还是谢谢。 有没有办法与使用 Spring Data JPA 完全相同? 对我有帮助,使用 spring-boot-starter-data-jdbc。也可能需要 CallableStatement #isWrapperFor() 和 #unWrap(),因为您的类路径可能正在使用代理 docs.microsoft.com/en-us/sql/connect/jdbc/reference/… @SJ.Jafari 我可以使用jdbc将数据存储在不同数据库的两个表中,如果我们更改表,第二个数据库中的第二个表也应该更改......我使用了2个连接obj并尝试从表一中获取所有数据并插入表二中,但这会导致一个问题;在表二中,数据总是产生重复的条目。【参考方案2】:

试试这个方法:

connection = dataSource.getConnection();
CallableStatement statement = connection.prepareCall("? = call dbo.RegisterInitAssets(?)");
statement.registerOutParameter(1, OracleTypes.CURSOR);//you can skip this if procedure won't return anything
statement.setObject(2, new InitvalsMsg()); //I hope you some kind of representation of this table in your project
statement.execute();

ResultSet set = (ResultSet) statement.getObject(1);//skip it too if its not returning anything

【讨论】:

好吧,我听从了你的建议,我得到:com.microsoft.sqlserver.jdbc.SQLServerException: The conversion from UNKNOWN to UNKNOWN is unsupported. 我编辑了问题并添加了相应的类,请看一下。 我已经编辑了答案,缺少参数号,现在试试吧 我对此进行了很多调查,最终发现无法使用 JDBC 传递 TVP,看看这个:blogs.msdn.com/b/jdbcteam/archive/2012/04/03/… 这个答案看起来适用于 Oracle,而不是 Microsoft SQL Server。

以上是关于使用 JDBC 将用户定义的表类型传递给 SQL Server 存储过程的主要内容,如果未能解决你的问题,请参考以下文章

将用户定义的表类型从 VBA 传递到 SQL

SQL Server中用户定义的表类型的性能

如何从 SQL Server 2014 中的 Select 查询中将数据分配给用户定义的表类型

MsSQL 未使用 sp_executesql 传递用户定义的表类型

SQL/C#:DataTable 到存储过程(从用户定义的表类型插入)- 转换错误

如何将用户定义的类型作为输入传递给存储过程?