简体   繁体   English

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

[英]Safely clearing Hibernate session in the middle of large transaction

I am using Spring+Hibernate for an operation which requires creating and updating literally hundreds of thousands of items. 我使用Spring + Hibernate进行操作,需要创建和更新数十万个项目。 Something like this: 像这样的东西:

{
   ...
   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
      }
   }
}

To protect myself against losing changes in the middle, I commit the changes immediately after 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);

At this point I have to say that entire process is running in a transaction wrapped into org.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter (yes, this is a webapp). 此时我不得不说整个进程在包含在org.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter中的事务中运行(是的,这是一个webapp)。

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. 这一切都运行良好,但有一个例外:在几千次更新/提交之后,整个过程变得非常慢,很可能是由于内存因Spring / Hibernate保留的不断增加的对象而膨胀。

In Hibernate-only environment this would be easily solvable by calling org.hibernate.Session#clear() . 在仅Hibernate环境中,通过调用org.hibernate.Session#clear()可以很容易地解决这个问题。

Now, the questions: 现在,问题:

  • When is it a good time to clear() ? 何时是clear()的好时机? Does it have big performance cost? 它的性能成本是否很高?
  • Why aren't objects like bar or baz released/GCd automatically? 为什么barbaz等对象不会自动释放/ GCd? 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? 如果答案是“Hibernate cache”,那么为什么缓存在可用内存不足时刷新?
  • is it safe/recommended to call org.hibernate.Session#clear() directly (having in mind entire Spring context, things like lazy loading, etc.)? 安全/建议直接调用org.hibernate.Session#clear() (考虑到整个Spring上下文,延迟加载等等)? Are there any usable Spring wrappers/counterparts for achieving the same? 是否有任何可用的Spring包装器/对应物来实现相同的目的?
  • If answer to the above question is true, what will happen with object foo , assuming clear() is called inside the loop? 如果对上述问题的回答是正确的,那么对象foo会发生什么,假设在循环内调用clear() What if foo.foo() is a lazy-load method? 如果foo.foo()是一个延迟加载方法怎么办?

Thank you for the answers. 谢谢你的答案。

When is it a good time to clear()? 何时是清除()的好时机? Does it have big performance cost? 它的性能成本是否很高?

At regular intervals, ideally the same as the JDBC batch size, after having flushed the changes. 在刷新更改后,定期(理想情况下与JDBC批处理大小相同)。 The documentation describes common idioms in the chapter about Batch processing : 该文档描述了有关批处理的章节中的常用习语:

13.1. 13.1。 Batch inserts 批量插入

When making new objects persistent flush() and then clear() the session regularly in order to control the size of the first-level cache. 在使新对象持久化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(); 

And this shouldn't have a performance cost , au contraire: 这不应该有性能成本 ,反对:

  • it allows to keep the number of objects to track for dirtiness low (so flushing should be fast), 它允许保持跟踪物体的数量低(因此冲洗应该很快),
  • it should allow to reclaim memory. 它应该允许回收记忆。

Why aren't objects like bar or baz released/GCd automatically? 为什么bar或baz等对象不会自动释放/ GCd? What's the point of keeping them in the session after the commit (in the next loop of iteration they're not reachable anyway)? 在提交之后将它们保留在会话中有什么意义(在下一个迭代循环中它们无论如何都无法访问)?

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). 如果您不想跟踪实体,那么您需要clear()clear()会话,这就是它的工作方式(人们可能希望在不“丢失”实体的情况下提交事务)。

But from what I can see, bar and baz instances should become candidate to GC after the clear. 但是从我所看到的情况来看,bar和baz实例应该在明确之后成为GC的候选者。 It would be interesting to analyze a memory dump to see what is happening exactly. 分析内存转储以查看正在发生的事情将会很有趣。

is it safe/recommended to call org.hibernate.Session#clear() directly 安全/建议直接调用org.hibernate.Session #clear()

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). 只要你flush()挂起的更改就不会松开它们(除非这是你想要的),我没有看到任何问题(你的当前代码会松动每100个循环创建一个但是它可能只是一些伪代码)。

If answer to the above question is true, what will happen with object foo, assuming clear() is called inside the loop? 如果对上述问题的回答是正确的,那么对象foo会发生什么,假设在循环内调用clear()? What if foo.foo() is a lazy-load method? 如果foo.foo()是一个延迟加载方法怎么办?

Calling clear() evicts all loaded instances from the Session , making them detached entities. 调用clear()Session驱逐所有已加载的实例,从而使它们成为分离的实体。 If a subsequent invocation requires an entity to be "attached", it will fail. 如果后续调用要求实体“附加”,则它将失败。

I just wanted to point out that, after clearing the session, if you want to continue to use some objects that were in the session, you will have to Session.refresh(obj) them in order to continue. 我只想指出,在清除会话后,如果要继续使用会话中的某些对象,则必须使用Session.refresh(obj)才能继续。

Otherwise you will get following error: 否则您将收到以下错误:

org.hibernate.NonUniqueObjectException

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM