休眠和批量更新

时间:2011-10-10 20:23:51

标签: hibernate jdbc batch-processing

虽然我可以看到很多围绕Hibernate和Batch Update的讨论,但这是我的具体情况,我希望得到所有专家的评论。 我正在遍历Document对象列表,对于每个文档,我需要使用Document对象中的属性更新DOCUMENT_METADATA表。

我可以使用标准的JDBC 2.0 Batch Update来执行此操作。但是,JDBC的使用将违背我在整个应用程序中使用Hibernate的软件标准,我讨厌做出例外。

为了使用Hibernate,我必须首先从Document对象中获取DocumentMetadata对象,我正在迭代Document对象,设置DocumentMetadata属性然后更新表。

我可以做类似

的事情
    for each document { 
    //fetch DocumentMetadata object given the id from Document 
    //invoke setter on DocumentMetadata object  
    em.persist(DocumentMetadata);
    if (count % 50 == 0) { 
    em.flush(); //flush a batch of updates and release memory:
    em.clear();
    }

}

对于n个记录(我将一次运行大约10,000条记录),这是最好的情况 我不是用上面的Hibernate方法做n choose = 1更新吗?鉴于我的表的大小(DOCUMENT_METADATA表有超过100列和100万条记录),我担心我会遇到与JDBC方法相关的性能问题。

任何建议?

由于 KOB

3 个答案:

答案 0 :(得分:3)

JPA,hibernate绝对支持批处理操作。做你用jdbc做的任何事情,但用this做。

答案 1 :(得分:2)

除了来自hvgotcodes的优秀建议(指向hibernate doc有关如何使用批量更新以及如何在没有内存问题的单个事务中处理数千个实体),您可以使用的优化是获取DocumentMetaData大块而不是一个接一个。

块大小也应与JDBC批处理大小相同。您只需要将N个文档ID拆分为(例如)20的块,并使用where id in (:idsOfChunk)子句。

最后,我想指出对persist的调用没有用处:附加了一个使用实体管理器从数据库加载的实体,并且对该实体所做的任何更改都会在flush时自动写入数据库时间。

答案 2 :(得分:2)

这是批量保存/更新的代码。我这样做是因为我们要插入1000个记录的批量限制。如果集合有25k个条目,它将以1000个批量插入。

private static int BATCH_SIZE = 1000; // current batch limit 

private void saveBulkEntries(
        final Collection<? extends MyObject> entries,
        final String insertSql) {
    if (entries.isEmpty()) {
        return;
    }

    // Create a new session independent of the current hibernate session
    // This avoids problems with the job transactions
    StatelessSession session = this.hibernateTemplate.getSessionFactory()
            .openStatelessSession();

    Transaction transaction = null;

    Long entryCounter = 0L;

    PreparedStatement batchUpdate = null;
    try {
        transaction = session.beginTransaction();
        batchUpdate = session.connection().prepareStatement(insertSql);

        for (MyObject entry : entries) {
            entry.addEntry(batchUpdate);
            batchUpdate.addBatch();

            if (++entryCounter % BATCH_SIZE == 0) {
                // Reached limit for uncommitted entries, so commit
                batchUpdate.executeBatch();
            }
        }

        // Commit any entries that have not been committed yet
        batchUpdate.executeBatch();
        batchUpdate.close();
        batchUpdate = null;
    }
    catch (HibernateException ex) {
        transaction.rollback();
        transaction = null;
    }
    catch (SQLException ex) {
        transaction.rollback();
        transaction = null;
    }
    finally {
        if (transaction != null) {
            transaction.commit();
        }

        if (batchUpdate != null) {
            try {
                batchUpdate.cancel();
                batchUpdate.close();
            }
            catch (SQLException ex) {

            }
        }

        session.close();
    }
}