2011-10-10 161 views
2

尽管我可以看到围绕Hibernate和批量更新进行的大量讨论,但这里是我的具体场景,我希望能够从所有专家那里获得评论。 我通过Document对象列表进行迭代,对于每个文档,我需要使用Document对象的属性更新DOCUMENT_METADATA表。休眠和批量更新

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

为了使用Hibernate,我必须首先从给定文档ID的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记录一次),作为 我没有做我n个最好的情况下,选择= 1个更新使用Hibernate的方法以上?鉴于我的表的大小(DOCUMENT_METADATA表有超过100列和100万条记录),我担心我会遇到comapred to JDBC方法的性能问题。

ANy suggestions?

感谢 KOB

+0

从技术上讲,你并不完全使用“休眠”。您正在使用JPA。你的JPA实现恰好是Hibernate。您可以在JPA上下文中提出这个问题。 – BalusC

回答

2

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

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

最后,我想指出,持久化调用没有用处:已使用实体管理器从数据库加载的实体已连接,并且对此实体所做的任何更改都会自动写入数据库冲洗时间。

+0

我刚刚意识到DocumentMetaData需要多个列而不是仅仅用Id来检索记录(它有多个PK)。我可以将一组DocumentMetaDataPK对象传递给(:idsOfChunk)吗?如果不是,当有多个密钥涉及时,是否还有其他策略可以将它分块存放? – phewataal

+0

是的,它应该工作。生成的SQL看起来如下所示:https://hibernate.onjira.com/browse/HSEARCH-306 –

+0

我尝试使用集合中的一个元素将DocumentMetaDataPK对象的集合传递给(:idsOfChunk)。 Hibernate吐出SQL作为select ...在(?,?,?,?)中从DOCMETADATA legacy0_ where(legacy0_.DOC_ID,legacy0_.DOC_TYPE_ID,legacy0_.DOC_REF_ID,legacy0_.EFF_DATE),但以某种方式抛出异常“意外的标记:,在语句中[select ..... ]”。我错过了那么明显吗? – phewataal

3

JPA,休眠绝对支持批量操作。做任何你会用jdbc做的事情,但是用this来做。

1

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

private static int BATCH_SIZE = 1000;

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(); 
    } 
} 
+0

更新了答案 – vsingh