一尘不染

在大型事务中安全清除Hibernate会话

hibernate

我将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(是的,这是一个webapp)。

这一切都很好,但有一个例外:经过数千次更新/提交后,整个过程确实变慢了,这很可能是由于Spring /
Hibernate保存的对象数量不断增加而导致内存膨胀。

在仅hibernate的环境中,可以通过调用轻松解决org.hibernate.Session#clear()

现在,问题是:

  • 什么时候是个好时机clear()?它有很大的性能成本吗?
  • 为什么没有自动喜欢barbaz释放/ GCd的对象?在提交后将它们保留在会话中有什么意义(在下一个迭代循环中,它们始终无法访问)?我没有做内存转储来证明这一点,但是我的好感觉是它们仍然存在直到完全退出。如果答案是“hibernate缓存”,那么为什么在可用内存变低时不刷新缓存?
  • org.hibernate.Session#clear()直接调用是否安全/建议(考虑到整个Spring上下文,诸如延迟加载之类的事情)?是否有任何可用的Spring包装器/配对部件可实现相同目的?
  • 如果对上述问题的回答是正确的,则foo假设clear()在循环内调用object会发生什么?如果foo.foo()是延迟加载方法怎么办?

谢谢你的回答。


阅读 240

收藏
2020-06-20

共1个答案

一尘不染

什么时候是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();

与此相反,这不应该 降低 性能 成本

  • 它可以使要跟踪的污物数量保持在较低水平(因此冲洗应该很快),
  • 它应该允许回收内存。

为什么不自动释放bar或baz之类的对象/ GCd?在提交后将它们保留在会话中有什么意义(在下一个迭代循环中它们始终无法访问)?

clear()如果您不想跟踪实体,就需要显式地进行会话,仅此而已(它可能是这样工作的(一个人可能想提交事务而不“丢失”实体))。

但是据我所知,bar和baz实例应在清除后成为GC的候选对象。分析内存转储以查看发生了什么会很有趣。

直接调用org.hibernate.Session#clear()是否安全/建议

只要您flush()有待执行的更改而不丢失它们(除非这是您想要的),我就不会发现任何问题(您当前的代码每100个循环会丢失一次create,但这也许只是一些伪代码)。

如果对上述问题的回答是正确的,假设在循环内调用了clear(),对象foo将会发生什么?如果foo.foo()是延迟加载方法怎么办?

调用clear()将所有加载的实例从中逐出Session,使它们成为分离的实体。如果后续调用要求实体被“附加”,它将失败。

2020-06-20