2017-06-13 59 views
2

我使用PreparedStatement使用單個查詢中插入多行:JDBC如何使用多個值查詢從單個插入中獲取所有生成的密鑰?

String query = "insert into MyTable (a,b,c) values (?,?,?),(?,?,?),(?,?,?),(?,?,?)"; // insert 4 rows in a single query 
PreparedStatement stmt = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); 
// .. here: loop to set all 4 x 3 values in my prepared statement 

而且我想在MSSQL數據庫生成的ID。我不確定execute使用哪種味道。

的​​方法不返回預期ResultSet

boolean thingy = stmt.execute(); 
System.out.println("execute returned ", thingy); // false: meaning no result set 
ResultSet result = stmt.getResultSet(); // result is null 

executeUpdate()方法只返回ResultSet

int rowCount = stmt.executeUpdate(); 
System.out.println(rowCount + " rows updated"); // 4 rows updated 
ResultSet result = stmt.getGeneratedKeys(); 
// there's only one result: 
result.next(); // true 
int ID1 = result.getInt(1); // good 
result.next(); // false, no more rows 

是否有可能與一個單一的INSERT得到生成的ID?出於性能原因,我不想發送多個INSERT查詢。

DB:MS SQL服務器11(2012)
網址爲:jdbc:JTDS:SQLSERVER:// ...
司機:net.sourceforge.jtds.jdbc.Driver JTDS版本1.3.1

+0

您正在使用哪種DBMS JDBC驅動程序? –

+0

更新後的驅動程序信息 – sebnukem

+0

jTDS驅動程序非常過時。它是否與微軟驅動程序一起工作? –

回答

1

在寫這個答案的時候,不JTDS也不是MSSQL-JDBC支持返回多個生成使用Statement.RETURN_GENERATED_KEYS鍵,無論是從使用TVC(表值構造函數)INSERT語句或使用executeBatchPreparedStatement。但是,下面的代碼可以使用兩種驅動程序(在寫作的時間JTDS 1.3.1和the dev branch of mssql-jdbc測試):

try (Statement st = conn.createStatement()) { 
    // TEST ENVIRONMENT: in a real application, this would be our permanent table 
    st.execute("CREATE TABLE #tmp (id INT IDENTITY PRIMARY KEY, txtcol NVARCHAR(50))"); 
    st.execute("INSERT INTO #tmp (txtcol) VALUES (N'existing data')"); 
    st.execute("INSERT INTO #tmp (txtcol) VALUES (N'more existing data')"); 
} 
StringBuilder sb = new StringBuilder(); 
sb.append("SET NOCOUNT ON; "); 
sb.append("DECLARE @seq INT, @txt NVARCHAR(50), @id INT; "); 
sb.append("DECLARE @newIds TABLE (seq INT, id INT); "); 
sb.append("DECLARE @toInsert TABLE (seq INT IDENTITY PRIMARY KEY, txt NVARCHAR(50)); "); 
sb.append(""); 
sb.append("INSERT INTO @toInsert (txt) VALUES (?), (?), (?); "); 
sb.append(""); 
sb.append("DECLARE crsr CURSOR FOR SELECT seq, txt FROM @toInsert ORDER BY seq; "); 
sb.append("OPEN crsr; "); 
sb.append("FETCH NEXT FROM crsr INTO @seq, @txt; "); 
sb.append("WHILE @@FETCH_STATUS = 0 "); 
sb.append("BEGIN "); 
sb.append(" INSERT INTO #tmp (txtcol) VALUES (@txt); "); 
sb.append(" SELECT @id = SCOPE_IDENTITY(); "); 
sb.append(" INSERT INTO @newIds (seq, id) VALUES (@seq, @id); "); 
sb.append(" FETCH NEXT FROM crsr INTO @seq, @txt; "); 
sb.append("END "); 
sb.append("SELECT id FROM @newIds ORDER BY seq; "); 
try (PreparedStatement ps = conn.prepareStatement(sb.toString())) { 
    ps.setString(1, "foo"); 
    ps.setString(2, "bar"); 
    ps.setString(3, "baz"); 
    try (ResultSet rs = ps.executeQuery()) { 
     while (rs.next()) { 
      System.out.println(rs.getInt(1)); 
     } 
    } 
} 

產生

3 
4 
5 
2

對於SQL Server 2008及更高版本,添加OUTPUT clause的INSERT語句,似乎這樣的伎倆:

try (Statement st = conn.createStatement()) { 
    // TEST ENVIRONMENT: in a real application, this would be our permanent table 
    st.execute("CREATE TABLE #tmp (id INT IDENTITY PRIMARY KEY, txtcol NVARCHAR(50))"); 
    st.execute("INSERT INTO #tmp (txtcol) VALUES (N'existing data')"); 
    st.execute("INSERT INTO #tmp (txtcol) VALUES (N'more existing data')"); 
} 
StringBuilder sb = new StringBuilder(); 
sb.append("INSERT INTO #tmp (txtcol) "); 
sb.append(" OUTPUT INSERTED.id "); 
sb.append(" VALUES (?), (?), (?) "); 
try (PreparedStatement ps = conn.prepareStatement(sb.toString())) { 
    ps.setString(1, "foo"); 
    ps.setString(2, "bar"); 
    ps.setString(3, "baz"); 
    try (ResultSet rs = ps.executeQuery()) { 
     while (rs.next()) { 
      System.out.println(rs.getInt(1)); 
     } 
    } 
} 

產生

3 
4 
5 
+0

這看起來很有前途(在Javascript中成功使用),但使用Java,我得到「'java.sql.SQLException:DML語句的目標表'MyTable'如果語句包含沒有INTO子句的OUTPUT子句,則不能有任何啓用的觸發器。「 – sebnukem

+0

我想我需要創建一個臨時表來放置輸出。 – sebnukem

+0

re:*「將輸出放入」*的臨時表 - 這基本上就是在其他答案的匿名代碼塊中發生的情況。 –

相關問題