正如前面问题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 设置,因此无法回滚。