JDBC驱动程序不支持批量更新并检索标识列。为什么?

时间:2014-11-22 15:29:30

标签: java sql-server jdbc

我的问题是我想做JDBC批量插入和检索标识列值。 MS SQL驱动程序不支持此功能。任何人都可以指导我,如何解决这个问题?

2 个答案:

答案 0 :(得分:5)

正如前面的问题here中所提到的,.getGeneratedKeys在SQL Server的.executeBatch之后根本不起作用。我刚刚确认使用最新版本的

仍然是这种情况
  • SQL Server JDBC驱动程序(4.1预览版)和
  • jTDS(1.3.1)

因此,您似乎只需要单独执行插入操作,而无需批处理。也就是说,而不是像这样的代码

String[] stringsToInsert = new String[] { "foo", "bar", "baz" };
try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO junk (textcol) VALUES (?)", 
        PreparedStatement.RETURN_GENERATED_KEYS)) {
    for (String s : stringsToInsert) {
        ps.setString(1, s);
        ps.addBatch();
    }
    ps.executeBatch();
    try (ResultSet rs = ps.getGeneratedKeys()) {
        while (rs.next()) {
            System.out.println(rs.getInt(1));
        }
    }
}

你需要使用这样的代码

String[] stringsToInsert = new String[] { "foo", "bar", "baz" };
try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO junk (textcol) VALUES (?)", 
        PreparedStatement.RETURN_GENERATED_KEYS)) {
    for (String s : stringsToInsert) {
        ps.setString(1, s);
        if (ps.executeUpdate() > 0) {
            try (ResultSet rs = ps.getGeneratedKeys()) {
                rs.next();
                System.out.println(rs.getInt(1));
            }
        }
    }
}

请注意,您仍然可以使用.setAutoCommit(false)并在事务中执行插入操作,而不是在批处理中。

至于为什么该操作不受支持,jTDS功能请求here已在九(9)年前提交,其中一个回复是

  

在决定是否值得付出努力之前,我必须先了解如何在jTDS中实现此功能。

由于jTDS和SQL Server JDBC驱动程序都没有实现它(至少现在没有;它对于Microsoft JDBC驱动程序来说是on the radar),也许对该功能的需求不足。

<强>附录

作为解决方法,我认为这可能有效

String[] stringsToInsert = new String[] { "foo", "bar", "baz" };
try (Statement s = conn.createStatement()) {
    s.executeUpdate(
            "CREATE TABLE #StuffToInsert (" +
                "id INT IDENTITY(1,1) PRIMARY KEY, " +
                "textcol NVARCHAR(100)" +
            ")");
}
try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO #StuffToInsert (textcol) VALUES (?)")) {
    for (String s : stringsToInsert) {
        ps.setString(1, s);
        ps.addBatch();
    }
    ps.executeBatch();
}
try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO junk (textcol) SELECT textcol FROM #StuffToInsert", 
        Statement.RETURN_GENERATED_KEYS)) {
    ps.executeUpdate();
    try (ResultSet rs = ps.getGeneratedKeys()) {
        while (rs.next()) {
            System.out.println(rs.getInt(1));
        }
    }
}

但遗憾的是.getGeneratedKeys只返回插入的最后一行的单个生成密钥。

如果通过网络连接发送大量单个(未批量)插入将是一个问题,那么这种解决方法可能会有所帮助:

String[] stringsToInsert = new String[] { "foo", "bar", "baz" };
try (Statement s = conn.createStatement()) {
    s.executeUpdate(
            "CREATE TABLE #StuffToInsert (" +
                "id INT IDENTITY(1,1) PRIMARY KEY, " +
                "textcol NVARCHAR(100)" +
            ")");
}
try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO #StuffToInsert (textcol) VALUES (?)")) {
    for (String s : stringsToInsert) {
        ps.setString(1, s);
        ps.addBatch();
    }
    ps.executeBatch();
}
try (PreparedStatement ps = conn.prepareStatement(
        "SET NOCOUNT ON; " +
        "DECLARE @GeneratedKeys TABLE(id INT IDENTITY(1,1) PRIMARY KEY, newkey INT); " +
        "DECLARE @text NVARCHAR(100); " +
        "DECLARE crsr CURSOR FOR " +
        "   SELECT textcol FROM #StuffToInsert ORDER BY id; " +
        "OPEN crsr; " +
        "FETCH NEXT FROM crsr INTO @text; " +
        "WHILE @@FETCH_STATUS = 0 " +
        "BEGIN " +
        "   INSERT INTO junk (textcol) VALUES (@text); " +
        "   INSERT INTO @GeneratedKeys (newkey) SELECT @@IDENTITY; " +
        "   FETCH NEXT FROM crsr INTO @text; " +
        "END " +
        "CLOSE crsr; " +
        "DEALLOCATE crsr; " +
        "SELECT newkey FROM @GeneratedKeys ORDER BY id; ")) {
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            System.out.println(rs.getInt(1));
        }
    }
}

但这种方法不尊重Java代码中的AutoCommit设置,因此无法进行回滚。

答案 1 :(得分:0)

这可能不是我认为的常见情况.....这就是为什么jdbc驱动程序不支持这一点。