2015-09-25 53 views
3

我有一個應用程序,我正在使用預處理語句將1000行批量寫入SQL Server數據庫。現在,如果任何這些插入失敗,我希望能夠將有問題的行寫入日誌文件,以便數據不會完全丟失。如果可能的話,我寧願只寫一條日誌失敗的行,而不是所有1000行,所以我的問題是這樣的:是否可以從Java PreparedStatement逐行插入?

如果在插入表時發生一行失敗,例如發生主鍵衝突,是整批阻止插入,還是隻有一行失敗?如果批處理失敗,是否可以在預處理語句中執行某種循環,逐個執行每個查詢,並以這種方式找出失敗的行,並僅記錄該行?或者是我實現這一目標的唯一方法,以便爲每個插入語句保留一個單獨的數組,並在批處理髮生故障時循環執行該操作?

更多的細節:

數據庫類型:SQL Server 2008中
連接庫:java.sql中
SQL語句:

Insert into Table(Column1,Column2) Values (Value1, Value2) 

Java代碼:

PreparedStatement prpStmt = dbConnection.prepareStatement(insertQuery.toString()); 
for (List lst : listOfValues){ 
    prpStmt.setString(1,lst[0]); 
    prpStmt.setString(2,lst[1]); 
    prpStmt.addBatch(); 
    dbCount++; 
    if (dbCount == DB_COUNT_LIMIT){ 
     try { 
      prpStmt.executeBatch(); 
      dbConnection.commit(); 
      dbCount = 0; 
      prpStmt.clearBatch(); 
     } catch (Exception e){ 
      for (PreparedStatement ps : prpStmt.getBatches()){ 
      if (!logToDBIndividually()) 
       logToFile(); 
      } 
     } 
    } 
} 
+0

您是否知道在任何給定時間內會處理多少記錄? –

+0

這取決於很多因素,其中包括涉及的數據庫,數據庫配置,JDBC驅動程序及其配置,JDBC連接配置,您準備好語句的SQL以及執行工作。如何命名數據庫並至少呈現模型代碼爲Java的Java代碼? –

+0

如果執行風格是按語句聲明,批處理的概念將無濟於事。在批次情況下,這些陳述被推在一起。您可以執行列表中的語句來完成此操作。 –

回答

1

如果在插入表時發生一行失敗,比如發生主鍵衝突,是整批阻止插入,還是隻有一行失敗?

仍然執行發生錯誤之前執行的語句。駕駛員可酌情決定批次中後面的陳述可能執行或可能未執行。假設您已經關閉了自動提交語句的連接,就像您在使用executeBatch()時應該這樣做並且看起來已經完成了一樣,無論是提交還是回滾已成功完成的更改都是您自己決定的。

無論駕駛員繼續過去失敗的說法,如果一個確實失敗,那麼你就可以判斷該批處理的語句均通過檢查所產生的BatchUpdateExceptiongetUpdateCounts()方法返回的數組成功執行。有關詳細信息,請參閱該方法的文檔或Statement.executeBatch()的文檔。

請注意您提供的示例代碼存在嚴重缺陷。如果您的某個更新失敗(例如拋出異常),則提交或回滾事務非常重要,並清除您不希望在下一次迭代中重新執行的任何批處理語句循環。此外,由於您想要處理BatchUpdateException與其他例外情況不同,因此很難找到普通的Exception,這在這裏尤其不合適。像這樣的東西可能會更好:

PreparedStatement prpStmt = dbConnection.prepareStatement(insertQuery.toString()); 
for (List lst : listOfValues){ 
    prpStmt.setString(1,lst[0]); 
    prpStmt.setString(2,lst[1]); 
    prpStmt.addBatch(); 
    dbCount++; 
    if (dbCount >= DB_COUNT_LIMIT) { // should not be >, but no harm in being safe 
     try { 
      prpStmt.executeBatch(); 
      dbConnection.commit(); 
      dbCount = 0; 
      prpStmt.clearBatch(); 
     } catch (BatchUpdateException bue){ 
      int[] updateCounts = bue.getUpdateCounts(); 

      if (updateCounts.length < dbCount) { 
       /* 
       * The first updateCounts.length statements (only) were 
       * executed successfully. The next one failed, and no more 
       * were attempted. 
       */ 
      } else { 
       /* 
       * The failed statements can be identified by having 
       * updateCounts[i] == Statement.EXECUTE_FAILED 
       */ 
      } 

      // Presumably you want to: 
      dbConnection.commit(); 

      // Maybe you want to: 
      dbCount = 0; 
      prpStmt.clearBatch(); 
      // Otherwise you need to do some other kind of cleanup/retry 
     } 

     /* 
     * no need to catch any other exception, including SQLException, in 
     * this scope, as it's unlikely that the overall bulk insertion can be 
     * continued after such an exception. 
     */ 
    } 
} 

如果批處理失敗,有可能是我做一些在準備好的聲明中循環,由一個執行每個查詢之一,並以這種方式,我可以找出失敗的那一行,然後只記錄那一行?

您可以確定如上所示失敗的語句。這將允許您記錄失敗。但是,您不能刪除詢問對象的當前批次,或者刪除除了通過clearBatch()以外的行,因此如果驅動程序碰巧是在第一個錯誤之後停止處理批次的類型,那麼從這樣的故障中恢復可能不是一個簡單的方法如你所願。雖然,所需的信息在那裏;使用索引for循環來遍歷列表而不是增強的for循環可能會更容易。

或者是唯一的方法來實現這個保持每個插入語句的單獨數組,並循環,如果批處理髮生故障?

不,這不是唯一的方法,但我可以想象出可以乾淨地實現的變體。然而,使用索引for循環,您可以確實恢復得非常乾淨。

+0

完美,謝謝你進入這樣的細節。我已經完全忘記了清除異常上的批處理,我敢肯定,這會讓我發瘋一段時間,試圖弄清楚什麼是錯誤的。 – dfader2

相關問題