只要不存在超过10.000个对象的批次,GORM便可以很好地使用。没有优化,您将面临outOfMemory问题。

常见的解决方案是对每个n(例如n = 500)个对象进行flush()和clear() session :

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()

但是有一些我无法解决的问题:
  • 如果我使用currentSession,则 Controller 将因为 session 为空而失败
  • 如果我使用sessionFactory.openSession(),则currentSession仍在FooService中使用。当然,我可以使用session.save(object)表示法。但这意味着,我必须修改fooService.doSomething()并为单个操作(常见的grails表示法,如fooObject.save())和批处理操作(session.save(fooObject()..表示法),复制代码。
  • 如果我使用Foo.withSession {session->}或Foo.withNewSession {session->},则Foo类的对象将按预期的方式由session.clear()清除。其他所有对象都不会被clear()清除,这会导致内存泄漏。
  • 我可以使用evict(object)手动清除 session 。但是由于关联的自动获取,几乎不可能获得所有相关对象。

  • 因此,我不知道如何在不使FooService.doSomething()更复杂的情况下解决问题。我正在寻找适用于所有域的withSession {}之类的东西。或者在开始时保存 session ( session tmp = currentSession),然后执行类似sessionFactory.setCurrentSession(tmp)的操作。两者都不存在!

    任何想法都是好主意!

    最佳答案

    我建议对这种批处理使用无状态 session 。看到这个帖子:Using StatelessSession for Batch processing

    10-06 01:22