2011-04-13 185 views
15

我需要處理一個CSV文件,併爲每個記錄(行)堅持一個實體。現在,我做這種方式:如何堅持很多實體(JPA)

while ((line = reader.readNext()) != null) { 
    Entity entity = createEntityObject(line); 
    entityManager.save(entity); 
    i++; 
} 

其中save(Entity)方法基本上只是一個EntityManager.merge()電話。 CSV文件中有大約20,000個實體(行)。這是做這件事的有效方法嗎?這似乎很慢。使用EntityManager.persist()會更好嗎?這種解決方案是否有任何缺陷?

編輯

這是一個漫長的過程(超過400秒)和我都嘗試解決方案,具有persistmerge。兩者都需要大致相同的時間才能完成(459s和443s)。問題是如果像這樣一個接一個地保存實體是最佳的。據我所知,Hibernate(這是我的JPA提供者)確實實現了一些緩存/刷新功能,所以我不必擔心這一點。

回答

11

JPA API沒有爲您提供所有選項以實現最佳效果。根據你想做這個的速度有多快,你將不得不尋找ORM特定的選項 - 休眠在你的情況。

檢查事項:您正在使用一個單一的交易

  1. 檢查(是的,顯然你肯定這一點)
  2. 檢查您的JPA提供商(休眠)使用JDBC批處理API(參考: hibernate.jdbc.batch_size)
  3. 檢查是否可以避開讓生成的密鑰(取決於DB/JDBC驅動程序,你從這個多少好處讓 - 是指:hibernate.jdbc.use_getGeneratedKeys)
  4. 檢查是否可以避開級聯邏輯(只有最低的性能優勢)

所以在Ebean ORM,這將是:

EbeanServer server = Ebean.getServer(null); 

    Transaction transaction = server.beginTransaction(); 
    try { 
     // Use JDBC batch API with a batch size of 100 
     transaction.setBatchSize(100); 
     // Don't bother getting generated keys 
     transaction.setBatchGetGeneratedKeys(false); 
     // Skip cascading persist 
     transaction.setPersistCascade(false); 

     // persist your beans ... 
     Iterator<YourEntity> it = null; // obviously should not be null 
     while (it.hasNext()) { 
      YourEntity yourEntity = it.next(); 
      server.save(yourEntity); 
     } 

     transaction.commit(); 
    } finally { 
     transaction.end(); 
    } 

哦,如果您通過原始JDBC做到這一點,你跳過ORM開銷(更少的對象創建/垃圾收集等) - 所以我不會」不要忽視那個選項。

所以是的,這不能回答你的問題,但可能會幫助你尋找更多的ORM特定的批量插入調整。

+0

你可以檢查hibernate.jdbc.batch_size和hibernate.jdbc.use_getGeneratedKeys(但不能設置每個事務)。 – 2011-04-14 05:18:31

3

你可以用傳統的SQL插入語句直接寫入數據庫。

@see EntityManager.createNativeQuery

+2

感謝您的投票下來,但爲什麼? – Ralph 2011-04-13 13:45:32

+1

在這種特殊情況下,原生查詢不會提供太多的加速。您只需將它們與批處理進行分組即可,您可以在JPA提供程序級別或JDBC驅動程序級別進行批處理。然而,在我的具體情況下,我可以使用INSERT INTO ... SELECT FROM ...組合,這將是一個巨大的加速,所以有我的+1。 – 2017-01-27 13:24:04

5

我認爲要做到這一點的常用方法是用交易。如果你開始一個新的事務,然後堅持大量的對象,它們將不會被插入數據庫,直到你提交事務。如果您有大量項目提交,這可以爲您帶來一些效率。

退房EntityManager.getTransaction

+1

它在事務中運行(使用Spring的@Transactional)。 – 2011-04-13 13:39:47

+0

您可以嘗試刪除註釋並查看性能是否改變。你也可以通過設置一箇中斷點並且在一些perist調用運行後檢查數據庫以確認這些行沒有被插入,從而確認它正在使用一次。可能是那個春天在10或100個電話之後提交,你可以做一些調整來改變性能。 – dough 2011-04-13 13:56:06

3

要讓它走得更快,至少在Hibernate中,你會做的flush()和清晰()一定數量的插件後。我已經爲數百萬條記錄完成了這個方法,並且它可以工作。它仍然很慢,但比不這樣做要快得多。基本結構是這樣的:

int i = 0; 
for(MyThingy thingy : lotsOfThingies) { 

    dao.save(thingy.toModel()) 

    if(++i % 20 == 0) { 
     dao.flushAndClear(); 
    } 

}