2012-12-06 154 views
5

只要沒有超過10,000個對象的批處理,GORM就可以正常工作。沒有優化,你將面臨outOfMemory問題。批量Grails休眠會話

的常見解決方案是沖洗()和清除()會話每個n(EGN = 500)對象:

Session session = sessionFactory.currentSession 
Transaction tx = session.beginTransaction(); 
def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP 

Date yesterday = new Date() - 1 

Criteria c = session.createCriteria(Foo.class) 
c.add(Restrictions.lt('lastUpdated',yesterday)) 
ScrollableResults rawObjects = c.scroll(ScrollMode.FORWARD_ONLY) 

int count=0; 
while (rawObjects.next()) { 
    def rawOject = rawObjects.get(0); 

    fooService.doSomething() 

    int batchSize = 500 
    if (++count % batchSize == 0) { 
     //flush a batch of updates and release memory: 
     try{ 
      session.flush(); 
     }catch(Exception e){ 
      log.error(session) 
      log.error(" error: " + e.message) 
      throw e 
     } 
     session.clear(); 
     propertyInstanceMap.get().clear() 
    } 
} 

session.flush() 
session.clear() 
tx.commit() 

但也存在一些問題,我不能解決:

  1. 如果我使用currentSession,那麼控制器由於會話爲空而失敗
  2. 如果我使用sessionFactory.openSession(),那麼currentSession仍然在FooService中使用。因爲我可以使用session.save(object)符號。但是這意味着我必須修改fooService.doSomething()併爲單個操作(fooObject.save())和批處理操作(session.save(fooObject())..表示法)複製代碼。
  3. 如果我使用Foo.withSession {session->}或Foo.withNewSession {session->},那麼Foo Class的對象按預期由session.clear()清除。所有其他對象不清除(),導致內存泄漏。
  4. 因爲我可以使用evict(object)來手動清除會話。但由於自動修飾聯繫,幾乎不可能得到所有相關的對象。

所以我不知道如何解決我的問題,而不是使FooService.doSomething()更復雜。我正在爲所有域尋找類似withSession {}的內容。或者在開始時保存會話(Session tmp = currentSession)並執行諸如sessionFactory.setCurrentSession(tmp)之類的操作。兩者都不存在!

任何想法都是好的!

+2

這看起來像應完全在服務方法中完成的工作。如果在服務方法中使用'currentSession',你的控制器是否仍然工作? – doelleri

+3

我同意@doelleri。服務是做這件事的好地方。此外,請記住,默認情況下它們是事務性的,如果您想手動處理狀態,請使用'Domain.withTransaction'並設置'static transactional = false'或讓服務處理提交/回滾。 –

+0

我在那裏發佈的代碼已經在服務方法中。是的,我可以使用服務方法的事務上下文,但它不能解決我的問題。 @doelleri - 你的問題的答案是:控制器剎車,這樣除了關閉瀏覽器之外,用戶不能在應用程序中做更多的事情。(見問題1) – Waldemar

回答

0

研究及改進你在做什麼是:

  1. 遍歷整個集合(rawObjects),並保存所有的ID對這些對象的列表。
  2. 循環顯示ID列表。在每次迭代中,通過它的id查找單個對象。

然後像現在一樣使用相同的週期性清除會話緩存。

順便說一下,someone else has suggested an approach similar to yours。但請注意,此鏈接中的代碼不正確;清除會話的行應該放在if語句中,就像在解決方案中一樣。