问题描述
我正在使用Spring + Hibernate进行一项操作,该操作需要创建和更新几十万个项目。像这样:
{
...
Foo foo = fooDAO.get(... );
for(int i = 0; i Bar bar = barDAO.load(i);
if(bar.needsModification()& foo.foo()){
bar.setWhatever(new whatever);
barDAO.update(bar);
// commit here
Baz baz = new Baz();
bazDAO.create(baz);
// if(i%100 == 0),clear
}
}
}
为了保护自己免受中间变更的损失,我立即在 barDAO.update(bar)
:
HibernateTransactionManager transactionManager = ...; //由Spring注入
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
transactionManager.commit(transactionStatus);
在这一点上,我必须说整个过程运行在一个事务中,包装到 org.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter
(是的,这是一个web应用程序)。
例外:在数千次更新/提交之后,整个过程变得非常缓慢,很可能是由于内存由于Spring / Hibernate保存的不断增加的对象而变得臃肿。
在仅限Hibernate的环境中,通过调用 org.hibernate.Session#clear()
很容易解决这个问题。
现在,问题:
明确()
?它有很大的性能成本吗?
bar
或 baz
自动发布/ GCd?在提交后将它们保留在会话中的重点是什么(在迭代的下一个循环中,它们无法访问)?我没有做内存转储来证明这一点,但我的良好感觉是,他们仍然在那里,直到完全退出。如果这个答案是Hibernate缓存,那么为什么缓存刷新可用内存不足?
org.hibernate.Session#clear()
直接(考虑到整个Spring上下文,例如延迟加载等)?是否有任何可用的Spring包装/对应物实现相同?
foo $ c $会发生什么c>,假设
clear()
在循环中被调用?如果 foo.foo()
是一个延迟加载方法? 感谢您的回答。
在刷新更改后,按照固定的时间间隔,理想情况下与JDBC批处理大小相同。该文档在有关的章节中描述常见习惯用法:
这不应该有一个性能 cost ,au contraire:
- 它允许保持对象的数量跟踪肮脏低(因此冲洗应该很快),
- 它应该允许回收记忆。
如果您不想保留实体,那么您需要明确地显示会话 clear()
这就是它的工作原理(人们可能希望在没有放弃实体)。
但是从我所看到的情况来看,bar和baz实例应该在明确之后成为GC的候选人。分析内存转储以查看到底发生了什么会很有趣。
只要您 flush()
改变不松散他们(除非这是你想要的),我没有看到任何问题(你当前的代码将失去一个创建每100循环,但也许它只是一些伪代码)。
$ b
调用清除所有已载入的实例从 I am using Spring+Hibernate for an operation which requires creating and updating literally hundreds of thousands of items. Something like this: To protect myself against losing changes in the middle, I commit the changes immediately after At this point I have to say that entire process is running in a transaction wrapped into This all works fine with one exception: after few thousand of updates/commits, entire process gets really slow, most likely due to memory being bloated by ever-increasing amount of objects kept by Spring/Hibernate. In Hibernate-only environment this would be easily solvable by calling Now, the questions: Thank you for the answers. At regular intervals, ideally the same as the JDBC batch size, after having flushed the changes. The documentation describes common idioms in the chapter about Batch processing: And this shouldn't have a performance cost, au contraire: Session
一个>,使他们分离的实体。如果后续调用需要实体附加,则会失败。 {
...
Foo foo = fooDAO.get(...);
for (int i=0; i<500000; i++) {
Bar bar = barDAO.load(i);
if (bar.needsModification() && foo.foo()) {
bar.setWhatever("new whatever");
barDAO.update(bar);
// commit here
Baz baz = new Baz();
bazDAO.create(baz);
// if (i % 100 == 0), clear
}
}
}
barDAO.update(bar)
:HibernateTransactionManager transactionManager = ...; // injected by Spring
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
transactionManager.commit(transactionStatus);
org.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter
(yes, this is a webapp).org.hibernate.Session#clear()
. clear()
? Does it have big performance cost?bar
or baz
released/GCd automatically? What's the point of keeping them in the session after the commit (in the next loop of iteration they're not reachable anyway)? I haven't done memory dump to prove this but my good feeling is that they're still there until completely exited. If the answer to this is "Hibernate cache", then why isn't the cache flushed upon the available memory going low?org.hibernate.Session#clear()
directly (having in mind entire Spring context, things like lazy loading, etc.)? Are there any usable Spring wrappers/counterparts for achieving the same?foo
, assuming clear()
is called inside the loop? What if foo.foo()
is a lazy-load method?
You need to clear()
the session explicitly if you don't want to keep entities tracked, that's all, that's how it works (one might want to commit a transaction without "loosing" the entities).
But from what I can see, bar and baz instances should become candidate to GC after the clear. It would be interesting to analyze a memory dump to see what is happening exactly.
As long as you flush()
the pending changes to not loose them (unless this is what you want), I don't see any problem with that (your current code will loose a create every 100 loop but maybe it's just some pseudo code).
Calling clear()
evicts all loaded instances from the Session
, making them detached entities. If a subsequent invocation requires an entity to be "attached", it will fail.
这篇关于在大事务中安全地清理Hibernate会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!