JDBC批量更新在初始更新语句集上跳过

时间:2015-05-12 20:05:31

标签: java jdbc oracle11g heap-memory batch-updates

我们在Java代码中使用JDBC批量更新(Statement - void addBatch(String sql)和int [] executeBatch())。该作业应该在表中插入大约27k条记录,然后在后续批次中更新大约18k条记录。

当我们的工作在早上6点运行时,它缺少几千条记录(我们从数据库审计日志中观察到这一点)。我们可以从作业日志中看到正在为所有18k记录生成更新语句。我们知道所有更新语句都按顺序添加到批处理中,但是,只有批处理开头的记录似乎丢失了。此外,它不是每天固定的数字 - 有一天,它跳过第一个4534更新语句,另一天它跳过前8853条记录,另一天,它跳过5648条记录。

我们最初认为这可能是一个线程问题,但由于跳过的块并不总是包含相同数量的更新语句,因此已经从思考过程中移开。如果我们假设在插入之前发生了前几千次更新,那么更新应至少显示在数据库审计日志中。但事实并非如此。

我们认为这是由于内存/堆问题,因为在任何其他时间运行作业会获取所有18k更新语句并且它们会成功执行。我们检查了Oracle数据库中的审计日志,并注意到在早上6点运行期间永远不会在表上执行缺少的更新语句。在任何其他时间,所有更新语句都显示在数据库审计日志中。

此作业已成功运行了近3年,此行为仅在几周前开始。我们试图查看服务器/环境的任何更改,但没有任何内容突然出现在我们面前。

我们正在努力查明为什么会发生这种情况,特别是,如果有任何进程占用了太多的JVM堆,结果是我们的更新语句被覆盖/未被执行。

数据库:Oracle 11g企业版11.2.0.3.0版 - 64位 Java:java版本" 1.6.0_51" Java(TM)SE运行时环境(版本1.6.0_51-b11) Java HotSpot(TM)服务器VM(内置20.51-b01,混合模式)

void main()
{
    DataBuffer dataBuffer;//assume that all the selected data to be updated is stored in this object
    List<String> TransformedList = transform(dataBuffer);
    int status = bulkDML(TransformedList);
}

public List<String> transform(DataBuffer i_SourceData) 
{
    //i_SourceData has all the data selected from 
    //the source table, that has to be updated
    List<Row> AllRows = i_SourceData.getAllRows();
    List<String> AllColumns = i_SourceData.getColumnNames();
    List<String> transformedList = new ArrayList<String>();
    for(Row row: AllRows)
    {
        int index = AllColumns.indexOf("unq_idntfr_col");
        String unq_idntfr_val = (String)row.getFieldValues().get(index);
        index = AllColumns.indexOf("col1");
        String val1 = (String)row.getFieldValues().get(index);
        String query = null;
        query =   "UPDATE TABLE SET col1 = " + val1 + " where unq_idntfr_col=" + unq_idntfr_val;//this query is not the issue either - it is parameterized in our code
        transformedList.add(query);
    }
    return transformedList;
}

public int bulkDML(List<String> i_QueryList)
{
    Connection connection = getConnection();
    Statement statement = getStatement(connection);
    try
    {
        connection.setAutoCommit(false);
        for (String Query: i_QueryList)
        {
            statement.addBatch(Query);
        }
        statement.executeBatch();   
        connection.commit();
    }
    //handle various exceptions and all of them return -1
    //not pertinent to the issue at hand
    catch(Exception e)
    {
            return -1;
    }
    CloseResources(connection, statement, null);
    return 0;
}

非常感谢任何建议,谢谢。

1 个答案:

答案 0 :(得分:0)

如果要在同一个表上执行多个更新,我建议修改您的查询以使用绑定和PreparedStatement,因为这实际上是使用Oracle数据库进行实际DML批处理的唯一方法。例如,您的查询将变为:

UPDATE TABLE SET col1=? WHERE unq_idntfr_col=?

然后使用JDBC批处理与相同的PreparedStatement。此更改需要您重新访问bulkDML方法,以使其将绑定值作为参数而不是SQL。

JDBC伪代码将如下所示:

     PreparedStatement pstmt = connection.prepareCall("UPDATE TABLE SET col1=? WHERE unq_idntfr_col=?");
     pstmt.setXXX(1, x);
     pstmt.setYYY(2, y);
     pstmt.addBatch();
     pstmt.setXXX(1, x);
     pstmt.setYYY(2, y);
     pstmt.addBatch();
     pstmt.setXXX(1, x);
     pstmt.setYYY(2, y);
     pstmt.addBatch();
     pstmt.executeBatch();
相关问题