2012-12-06 152 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语句中,就像在解决方案中一样。