【问题标题】:Hibernate: flush() and commit()休眠:flush() 和 commit()
【发布时间】:2013-01-12 23:05:19
【问题描述】:

单独打电话给org.hibernate.Session.flush() 是个好习惯吗?

正如org.hibernate.Session docs 中所说,

必须在工作单元结束时调用,在提交事务和关闭会话之前(取决于刷新模式,Transaction.commit() 调用此方法)。

如果org.hibernate.Transaction.commit() 已经可以调用flush(),您能否解释一下调用flush() 的目的?

【问题讨论】:

  • 您可以使用 @Transactional 注释注入 sessionFactory,如果您查看了它。你不需要你的代码在任何地方都是事务性的(但在某些情况下需要)。
  • 我刚刚在我的代码中遇到了一个有趣的情况,我不得不合并一个已经从客户端应用程序生成的 id 的实体,然后刷新同一个实体以获取数据库生成的字段,例如日期创建和修改。如果不调用 session.flush() 它会抛出一个找不到对象的异常,因为有时直到事务结束才会忽略合并调用,当我在合并调用之后添加刷新时,它会强制休眠执行实际查询,然后再刷新该对象有效,因为它现在存在于数据库中!

标签: java hibernate orm


【解决方案1】:

在 Hibernate Manual 你可以看到这个例子

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

如果不调用 flush 方法,你的一级缓存会抛出 OutOfMemoryException

Also you can look at this post about flushing

【讨论】:

  • 可以参考这篇文章吗?
  • 这个答案给出了一个特定的情况,在这种情况下明确调用 flush 是个好主意,但我认为它没有解决是否应该总是这样做的一般问题。
  • 嗨@KorayTugay。我很长一段时间没有使用新版本的hibernate的经验。但是以前没有 clean 你可能会收到一些意想不到的结果。所以我建议尝试不同的方法,甚至查看最新的文档
【解决方案2】:

flush() 会将您的数据库与内存中保存的对象/对象的当前状态同步,但它不会提交事务。因此,如果在调用flush() 后出现任何异常,则事务将回滚。 您可以使用flush() 将您的数据库与小块数据同步,而不是使用commit() 一次提交大量数据,并面临获得OutOfMemoryException 的风险。

commit() 将使存储在数据库中的数据永久化。一旦commit() 成功,您将无法回滚您的事务。

【讨论】:

    【解决方案3】:

    显式刷新的一个常见情况是,当您创建一个新的持久实体并希望它生成并分配一个人工主键,以便您以后可以在同一事务中使用它。在这种情况下,调用 flush 会导致您的实体获得一个 id。

    另一种情况是,如果一级缓存中有很多东西,并且您想定期清除它(以减少缓存使用的内存量),但您仍想提交整件事在一起。 Aleksei's answer 就是这种情况。

    【讨论】:

    • 如果您想立即访问生成的PK,我相信您还需要致电.refresh(...)
    【解决方案4】:

    flush(); 刷新是将底层持久存储与内存中保存的持久状态同步的过程。它将更新或插入正在运行的事务中的表中,但它可能不会提交这些更改。

    您需要在批处理中刷新,否则可能会给出 OutOfMemoryException。

    Commit(); Commit 将使数据库提交。当你有一个持久化的对象并且你改变了它的值时,它会变脏,hibernate 需要将这些更改刷新到你的持久层。因此,您应该提交,但它也会结束工作单元 (transaction.commit())。

    【讨论】:

      【解决方案5】:

      除非必要,通常不建议显式调用flush。 Hibernate 通常在事务结束时自动调用 Flush,我们应该让它完成它的工作。现在,在某些情况下,您可能需要显式调用 flush,其中第二个任务取决于第一个 Persistence 任务的结果,两者都在同一个事务中。

      例如,您可能需要持久化一个新实体,然后使用该实体的 ID 在同一事务中执行一些其他任务,在这种情况下,需要先显式刷新该实体。

      @Transactional
      void someServiceMethod(Entity entity){
          em.persist(entity); 
          em.flush() //need to explicitly flush in order to use id in next statement
          doSomeThingElse(entity.getId());    
      }
      

      还要注意,显式刷新不会导致数据库提交,数据库提交仅在事务结束时完成,因此如果在调用刷新后发生任何运行时错误,更改仍将回滚。

      【讨论】:

        【解决方案6】:

        默认情况下,刷新模式为 AUTO,这意味着:“有时会在查询执行之前刷新会话,以确保查询永远不会返回陈旧状态”,但大多数情况下,当您提交更改时会刷新会话。当您使用 FlushMode=MANUAL 或想要进行某种优化时,手动调用 flush 方法很有用。但是我从来没有这样做过,所以我不能给你实用的建议。

        【讨论】:

          【解决方案7】:

          session.flush() 是同步方法,意思是按顺序往数据库中插入数据。如果我们使用这个方法,数据不会存入数据库,而是存入缓存,如果中间出现异常我们可以处理。 但是commit()会在数据库中存储数据,如果我们存储更多的数据,那么可能会出现内存异常,就像保存点主题中的JDBC程序一样

          【讨论】:

            猜你喜欢
            • 2019-11-23
            • 1970-01-01
            • 2011-08-06
            • 1970-01-01
            • 2014-09-15
            • 2012-06-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多