【问题标题】:JDBC driver doesn't support batch update with retrieval of identity column. Why?JDBC 驱动程序不支持通过检索标识列进行批量更新。为什么?
【发布时间】:2015-01-20 15:27:45
【问题描述】:

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

【问题讨论】:

  • 您是否尝试过使用Statement#getGeneratedKeys
  • 显而易见的解决方案:不要使用批量插入,而是在循环中执行普通的executeUpdategetGeneratedKeys
  • @LuiggiMendoza SQL Server JDBC 驱动程序不支持批量执行的插入(参考:here)。我刚刚用 sqljdbc41.jar(版本 4.1 预览版,最新版本,AFAIK)进行了测试,显然情况仍然如此。 :(
  • 另一个选项是不使用生成的密钥并生成密钥应用程序端(即uuid)。
  • Microsoft 的 SQL Server JDBC 驱动程序团队已将其识别为 feature request for a future release

标签: java sql-server jdbc


【解决方案1】:

正如前面问题here 中提到的,.getGeneratedKeys.executeBatch 之后对于 SQL Server 根本不起作用。我刚刚确认使用最新版本的情况仍然如此

  • 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) 并在 transaction 中执行插入操作,但不能在 batch 中执行。

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

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

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

附录

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

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 设置,因此无法回滚。

【讨论】:

  • 首先,为什么在IDENTITY的情况下,批量执行需要密钥检索?
【解决方案2】:

这可能不是我认为的常见情况.....这就是 jdbc 驱动程序不支持它的原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 2012-11-26
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多