我将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):
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(是的,这是一个webapp)。
org.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter
这一切都很好,但有一个例外:经过数千次更新/提交后,整个过程确实变慢了,这很可能是由于Spring / Hibernate保存的对象数量不断增加而导致内存膨胀。
在仅hibernate的环境中,可以通过调用轻松解决org.hibernate.Session#clear()。
org.hibernate.Session#clear()
现在,问题是:
clear()
bar
baz
foo
foo.foo()
谢谢你的回答。
什么时候是clear()的好时机?它有很大的性能成本吗?
刷新更改后,应按固定的时间间隔,最好与JDBC批处理大小相同。该文档在有关批处理的章节中描述了常见的成语:
13.1。批量插入 在使新对象持久化flush()然后定期清除()时,会话将控制一级缓存的大小。 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();
13.1。批量插入
在使新对象持久化flush()然后定期清除()时,会话将控制一级缓存的大小。
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或baz之类的对象/ GCd?在提交后将它们保留在会话中有什么意义(在下一个迭代循环中它们始终无法访问)?
clear()如果您不想跟踪实体,就需要显式地进行会话,仅此而已(它可能是这样工作的(一个人可能想提交事务而不“丢失”实体))。
但是据我所知,bar和baz实例应在清除后成为GC的候选对象。分析内存转储以查看发生了什么会很有趣。
直接调用org.hibernate.Session#clear()是否安全/建议
只要您flush()有待执行的更改而不丢失它们(除非这是您想要的),我就不会发现任何问题(您当前的代码每100个循环会丢失一次create,但这也许只是一些伪代码)。
flush()
如果对上述问题的回答是正确的,假设在循环内调用了clear(),对象foo将会发生什么?如果foo.foo()是延迟加载方法怎么办?
调用clear()将所有加载的实例从中逐出Session,使它们成为分离的实体。如果后续调用要求实体被“附加”,它将失败。
Session