休眠:flush()和commit()


77

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

org.hibernate.Session文档中所述,

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

您能解释一下flush()是否org.hibernate.Transaction.commit()会明确呼叫的目的吗?


您可以sessionFactory使用@Transactional注解注入,如果您查看了注解。从此以后,您将不需要代码在任何地方都具有事务性(尽管在某些情况下需要这样做)。
狮子

我在代码中遇到了一个有趣的情况,我不得不合并一个已经具有从客户端应用程序生成的ID的实体,然后刷新相同的实体以获取数据库生成的字段,例如创建和修改的日期。如果不调用session.flush(),它将引发未找到对象的异常,因为有时会忽略合并调用,直到事务结束为止,当我在合并调用之后立即添加flush时,它将强制休眠执行实际查询,然后刷新该对象有效,因为它现在存在于数据库中!
Japheth Ongeri-inkalimeva

Answers:


96

在《 Hibernate手册》中,您可以看到此示例

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

您也可以看一下有关冲洗的帖子


你可以参考这篇文章吗?
bsiamionau

5
这个答案给出了一个特定的情况,在该情况下,显式调用flush是个好主意,但我认为它不能解决是否应该始终这样做的一般问题。
Dave L.

嗨@KorayTugay。我很长时间没有使用新版本的休眠的经验。但是以前如果不清洁,可能会收到一些意外的结果。因此,我建议尝试不同的办法,甚至查看最新的文档
阿列克谢Bulgak

80

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

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


54

显式刷新的一种常见情况是,当您创建一个新的持久性实体时,您希望它具有生成并分配给它的人工主键,以便以后可以在同一事务中使用它。在这种情况下,调用flush将导致为您的实体提供一个ID。

另一种情况是,如果一级缓存中有很多东西,而您想定期清除它(以减少缓存使用的内存量),但您仍然想将整个东西一起提交。Aleksei的答案就是这种情况。


1
如果你想有机会获得立即生成的PK,我相信你也需要调用.refresh(...)
萨姆·贝瑞

12

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

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

Commit();提交将使数据库提交。当具有持久对象并更改其值时,该对象将变脏,休眠状态需要将这些更改刷新到持久层。因此,您应该提交但也要结束工作单元(transaction.commit())。


8

除非必要,通常不建议显式调用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());    
}

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


4

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


2

session.flush()是同步方法,意味着将数据顺序插入数据库。如果使用此方法,数据将不会存储在数据库中,但会存储在缓存中,如果中间出现任何异常,我们可以处理。但是commit()会将数据存储在数据库中,如果那么我们要存储更多的数据,则可能有机会摆脱内存异常,就像在Save point主题中的JDBC程序中一样

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.