java - 在大型事务中安全清除 Hibernate session

标签 java hibernate spring orm

我正在将 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)。

这一切都很好,但有一个异常(exception):在几千次更新/提交之后,整个过程变得非常缓慢,这很可能是由于 Spring/Hibernate 保存的对象数量不断增加而导致内存膨胀。

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

现在,问题:

  • 什么时候是clear()的好时机?性能开销大吗?
  • 为什么像 barbaz 这样的对象不能自动释放/GCd?在提交之后将它们保留在 session 中有什么意义(在下一个迭代循环中它们无论如何都无法访问)?我还没有做内存转储来证明这一点,但我的感觉是它们仍然存在,直到完全退出。如果这个问题的答案是“hibernate 缓存”,那么为什么在可用内存变低时不刷新缓存?
  • 直接调用 org.hibernate.Session#clear() 是否安全/推荐(考虑整个 Spring 上下文,诸如延迟加载等)?是否有任何可用的 Spring 包装器/对应物来实现相同的目标?
  • 如果上述问题的答案是正确的,假设 clear() 在循环内被调用,对象 foo 会发生什么?如果 foo.foo() 是延迟加载方法怎么办?

感谢您的回答。

最佳答案

When is it a good time to clear()? Does it have big performance cost?

在刷新更改后,定期进行,理想情况下与 JDBC 批处理大小相同。文档在关于 Batch processing 的章节中描述了常见的习语。 :

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.

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();

这不应该有性能成本,相反:

  • 它允许将跟踪脏污的对象数量保持在低水平(因此刷新应该很快),
  • 它应该允许回收内存。

Why aren't objects like 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)?

如果您不想跟踪实体,您需要显式clear() session ,仅此而已,这就是它的工作原理(可能希望提交事务而不“丢失”实体)。

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

is it safe/recommended to call org.hibernate.Session#clear() directly

只要您 flush() 待处理的更改不会丢失它们(除非这是您想要的),我看不出有任何问题(您当前的代码将丢失创建每 100 个循环,但也许它只是一些伪代码)。

If answer to the above question is true, what will happen with object foo, assuming clear() is called inside the loop? What if foo.foo() is a lazy-load method?

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

关于java - 在大型事务中安全清除 Hibernate session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3788048/

相关文章:

java - 如何通过 spring @RestController 提供压缩文件下载?

java - 安卓 : Get String from TextView and pass it to other few activities

spring - CallableTaskletAdapter Spring Batch

Java Spring MongoDB 存储库接口(interface)

java - 使用 LEFT JOIN 和 GROUP BY 的 JPQL 查询

java - 如何测试延迟加载的 JPA 集合是否已初始化?

hibernate - PostgreSQL Money 类型::如何将其映射到 Hibernate JPA?

java - 如何让方形星星图案看起来像棋盘?

java - 将 Eclipse 插件项目转换为 Eclipse RCP 应用程序

java - 在 matcher.replace 方法中,如何限制替换次数?