我正在使用Grails 1.1 beta2。我需要将大量数据导入Grails应用程序。如果我反复实例化grails域类然后保存它,则性能会降低到无法接受的程度。以从电话簿导入人为例:
for (each person in legacy phone book) { // Construct new Grails domain class from legacy phone book person Person person = new Person(...) person.save() }
事实证明这是缓慢的。Grails邮件列表上的某人建议在事务中分批保存。所以现在我有:
List batch = new ArrayList() for (each person in legacy phone book) { // Construct new Grails domain class from legacy phone book person Person person = new Person(...) batch.add(person) if (batch.size() > 500) { Person.withTransaction { for (Person p: batch) p.save() batch.clear() } } } // Save any remaining for (Person p: batch) p.save()
这必须至少在开始时更快。每笔交易会保存500条记录。随着时间的流逝,交易花费的时间越来越长。最初的几笔交易大约需要5秒钟,然后才从那里开始。经过约100笔交易,每笔交易都要花一分钟多的时间,这再次令人无法接受。更糟糕的是,最终Grails最终将耗尽Java堆内存。我可以增加JVM堆大小,但这只会延迟OutOfMemoryError异常。
OutOfMemoryError
任何想法为什么会这样?就像有一些内部资源没有被释放。性能变得更糟,内存被保留,然后最终系统内存不足。
根据Grails文档,withTransaction将闭包传递给Spring的TransactionStatus对象。我找不到任何东西TransactionStatus来完成/结束交易。
withTransaction
TransactionStatus
编辑: 我正在从Grails的控制台(grails console)运行此文件
grails console
编辑: 这是内存不足异常:
Exception thrown: Java heap space java.lang.OutOfMemoryError: Java heap space at org.hibernate.util.IdentityMap.entryArray(IdentityMap.java:194) at org.hibernate.util.IdentityMap.concurrentEntries(IdentityMap.java:59) at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:113) at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65) at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26) at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000) at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338) at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106) at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:655) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:732) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701) at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
这是所有hibernate应用程序的常见问题,它是由hibernate会话的增长引起的。我猜想grails控制台会为您打开一个hibernate会话,其方式类似于我知道它用于普通Web请求的“视图中打开会话”模式。
解决方案是掌握当前会话,并在每次批处理后清除它。我不确定如何使用控制台来获取spring bean,通常对于控制器或服务,只需将它们声明为成员即可。然后,您可以使用获取当前会话sessionFactory.getCurrentSession()。为了清除它,只需调用session.clear(),或者session.evict(Object)对每个Person对象有选择地使用。
sessionFactory.getCurrentSession()
session.clear()
session.evict(Object)
Person
对于控制器/服务:
class FooController { def sessionFactory def doStuff = { List batch = new ArrayList() for (each person in legacy phone book) { // Construct new Grails domain class from legacy phone book person Person person = new Person(...) batch.add(person) if (batch.size() > 500) { Person.withTransaction { for (Person p: batch) p.save() batch.clear() } // clear session here. sessionFactory.getCurrentSession().clear(); } } // Save any remaining for (Person p: batch) p.save() } } }
希望这可以帮助。