如果发生异常(SQLException | BatchUpdateException),则重试preparedstatement批处理 - Java

Posted

技术标签:

【中文标题】如果发生异常(SQLException | BatchUpdateException),则重试preparedstatement批处理 - Java【英文标题】:Retry the preparedstatement batches if an exception (SQLException | BatchUpdateException) occurs - Java 【发布时间】:2020-07-20 15:29:58 【问题描述】:

我的代码遇到了一些事务的 SQL Server 死锁问题。所以,我实现了一个重试逻辑来克服同样的问题。现在,我面临一个新问题。问题是,每当它重试时,尝试执行的批处理在从异常中恢复后将被清空/清除。这导致缺少数据插入/更新。请帮我解决这个问题。

总结: 如果发生异常(SQLException | BatchUpdateException),则重试preparedstatement批处理

当前实施:

do 
        try 
            if (isInsert) 
                int[] insertRows = psInsert.executeBatch();
                psInsert.clearBatch();
                System.out.println("insertRowsSuccess:" + Arrays.toString(insertRows));
             else 
                int[] updateRows = psUpdate.executeBatch();
                psUpdate.clearBatch();
                System.out.println("updateRowsSuccess:" + Arrays.toString(updateRows));
            
            break;
         catch (BatchUpdateException e) 
            conn.rollback();
            if (++count == maxTries) 
                System.out.println(e.getMessage());
                getFailedRecords(e, operation);
            
         catch (SQLException e) 
            if (++count == maxTries) 
                System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
            
        
        System.out.println("Tries:" + count);
     while (true);
    

private static void getFailedRecords(BatchUpdateException ex, String operation) 

    int[] updateCount = ex.getUpdateCounts();
    ArrayList<Integer> failedRecsList = new ArrayList<Integer>();
    int failCount = 0;

    for (int i = 0; i < updateCount.length; i++) 
        if (updateCount[i] == Statement.EXECUTE_FAILED) 
            failCount++;
            failedRecsList.add(i);
        

    

    System.out.println(operation + " Failed Count: " + failCount);
    System.out.println(operation + " FailedRecordsIndex:" + failedRecsList);

       

【问题讨论】:

【参考方案1】:

执行后,无论批处理成功与否,批处理都会被清除。您需要重新填充批次,然后才能重试。

【讨论】:

非常感谢马克。我最近一直在考虑这个解决方案。但是,在当前系统/模式中不允许我重建批处理。我希望以某种方式保留批次,在执行之前克隆它,或者在第二次尝试中失败,以便它捕获异常。在这种情况下,它只是执行并在重试时返回一个空数组。因此,重试时不会捕获到异常。 @sree_me 如果无法重建批处理,最好单独执行语句。 我尝试了你的建议,效果很好!谢谢。【参考方案2】:

我认为您应该将clearBatch() 移到try 之外。如果您必须重新检查它是插入还是更新,那就这样吧。如果psInsertpsUdate 是同一个类,或者派生自同一个基类(听起来应该如此),您可以轻松地在单独的一个线性方法中调用它。

我还认为,如果您将此问题提交给code review,您会得到一些改进代码的好建议。我并不是说这很糟糕,但我认为底层模型还有改进的空间。不过,我对 Java 还不够熟悉。

【讨论】:

感谢您的回复。我会试试你的建议。我也提交了相同的代码审查。希望我能尽快得到解决方案/提示。 A executeBatch 将在返回之前自动清除批次。在try 内有一个clearBatch() 的事实是无关紧要的:当BatchUpdateException 被抛出时它不会被执行。要重试,需要再次填充该批次。

以上是关于如果发生异常(SQLException | BatchUpdateException),则重试preparedstatement批处理 - Java的主要内容,如果未能解决你的问题,请参考以下文章

JDBC异常

java.sql.SQLException:Io 异常:套接字读取超时与关闭连接

SqlException,提供者:TCP 提供者,错误:35 - 捕获到内部异常)

异常和 SQLException 的区别

内部异常:java.sql.SQLException:事务无法继续STATUS_COMMITTED

休眠:java.sql.SQLException:事务回滚后尝试继续工作