我正在使用 Spring+Hibernate 进行一项需要创建和更新数十万个项目的操作。像这样的东西:
{
...
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
中的事务中运行(是的,这是一个 Web 应用程序)。
这一切都工作得很好,但有一个例外:在几千次更新/提交之后,整个过程变得非常慢,很可能是由于 Spring/Hibernate 保留的对象数量不断增加而导致内存膨胀。
在仅限 Hibernate 的环境中,可以通过调用
org.hibernate.Session#clear()
轻松解决此问题。
现在,问题:
clear()
的好时机?性能成本大吗?bar
或 baz
这样的对象不会自动释放/GCd?提交后将它们保留在会话中有何意义(在下一个迭代循环中它们无论如何都无法访问)?我还没有进行内存转储来证明这一点,但我的良好感觉是它们仍然存在,直到完全退出。如果答案是“休眠缓存”,那么为什么在可用内存变低时不刷新缓存?org.hibernate.Session#clear()
是否安全/建议(考虑到整个Spring上下文,例如延迟加载等)?是否有任何可用的 Spring 包装器/对应物可以实现相同的目的?foo
,对象 clear()
会发生什么情况?如果 foo.foo()
是延迟加载方法怎么办?谢谢您的回答。
什么时候是
的好时机?性能成本大吗?clear()
刷新更改后,定期进行,最好与 JDBC 批量大小相同。该文档在有关批处理的章节中描述了常见的习语:
13.1。批量插入
使新对象持久化时 刷新()然后清除()会话 定期以控制尺寸 一级缓存。
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for ( int i=0; i<100000; i++ ) { Customer customer = new Customer(.....); session.save(customer); if ( i % 20 == 0 ) { //20, same as the JDBC batch size //flush a batch of inserts and release memory: session.flush(); session.clear(); } } tx.commit(); session.close();
这不应该有性能成本,相反:
为什么像
或bar
这样的对象不会自动释放/GCd?提交后将它们保留在会话中有何意义(在下一个迭代循环中它们无论如何都无法访问)?baz
如果您不想跟踪实体,则需要显式地
clear()
会话,仅此而已,这就是它的工作原理(人们可能希望在不“丢失”实体的情况下提交事务)。
但从我看来,
bar
和baz
实例在清除后应该成为GC的候选者。分析内存转储以查看到底发生了什么会很有趣。
安全/建议直接致电
org.hibernate.Session#clear()
只要您
flush()
待处理的更改不丢失它们(除非这是您想要的),我没有看到任何问题(您当前的代码将每 100 个循环丢失一个创建,但也许这只是一些伪代码)。
如果上述问题的答案为真,假设在循环内调用
,对象foo
会发生什么情况?如果clear()
是延迟加载方法怎么办?foo.foo()
clear()
会从 Session
中逐出所有已加载的实例,使它们成为分离的实体。如果后续调用需要“附加”实体,则会失败。
我只是想指出,清除会话后,如果您想继续使用会话中的某些对象,则必须
Session.refresh(obj)
才能继续。
否则你会得到以下错误:
org.hibernate.NonUniqueObjectException