Hibernate中的session.flush()有什么用处?

135

在更新记录时,我们可以使用Hibernate中的session.flush()。那么flush()的作用是什么?

9个回答

171

刷新session会强制Hibernate将Session 中的内存状态与数据库同步(即将更改写入数据库)。 默认情况下,Hibernate会自动为您刷新更改:

  • 在某些查询执行之前
  • 当提交事务时

允许显式刷新Session提供了更精细的控制,这在某些情况下可能是必需的(例如获取ID分配、控制Session的大小等)。


11
请注意,本答案描述了默认的Hibernate行为:刷新行为可以通过Flush Mode设置进行更改。详细信息请参见http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/FlushMode.html(版本3.5)。 - SteveT
1
我找到了这个文档,里面确切地说了你所说的,但我有一个问题,那就是优先级是什么意思,比如:1)我有一个类,在使用代码id = session.save(obj);保存对象时,事务在下一行被提交,但是obj没有保存到数据库中,为什么?2)我使用session.save(obj);保存了obj,并在返回时使用return obj.getprimaryID();,在这种情况下,obj已保存到数据库中。那么为什么会出现这种行为? - Amogh

99

正如以上答案中所说的那样,通过调用flush(),我们可以强制Hibernate在数据库上执行SQL命令。但请注意,更改尚未“提交”。

因此,在执行commit之前执行flush()并直接访问数据库(例如从SQL提示符)并检查修改后的行时,您将不会看到更改。

这与打开两个SQL命令会话相同。只有在提交更改后,其他会话才能看到已经完成的更改。


15
好的——变化可能会稍微可见。例如,未提交的行可以在插入但未提交的行上创建锁,并延迟同一行被另一个会话插入,直到事务提交或回滚。因此,它并不完全看不见。 - rghome
5
我已经在整个网络中搜索过,这是终于让我理解的答案。谢谢。 - Siddhartha
在for循环中使用.flush()有什么用处,如果commit()在最后执行flush? - Heetola
@Kaushik Lele 如果数据在 flush() 后不可见,那么 flush() 的意义是什么?您能详细说明一些更精细的用例吗? - java_geek

34

我只知道当我们调用session.flush()时,语句会在数据库中执行但不被提交。

假设我们不在session对象上调用flush()方法,而是在调用commit方法时,它会在内部执行将语句提交到数据库然后进行提交的工作。

commit=flush+commit (在功能方面)

因此,我得出结论,当我们在Session对象上调用flush()方法时,它不会被提交,但会在数据库中执行查询并发生回滚。

为了提交,我们使用Transaction对象上的commit()方法。


1
你能详细说明一下 flush 的必要性吗? - java_geek

16

你能提供一个用户应该担心顺序的场景吗?使用Hibernate的目的是使与数据库相关的事情对用户透明。当我们执行“commit”时,刷新会自动发生。在什么情况下您会执行刷新但不提交呢? - Kaushik Lele
1
@KaushikLele,您可以参考这个问题:http://stackoverflow.com/questions/37382872/spring-transactional-annotation-isolation-not-working-for-read-uncommitted - Sam YC

11
您可以使用flush强制执行验证约束条件,以在已知位置进行检测,而不是在事务提交时进行检测。也许某些框架逻辑、声明性逻辑、容器或模板隐式调用了commit。在这种情况下,任何抛出的异常可能很难捕获和处理(它可能太高在代码中)。
例如,如果您保存一个具有地址唯一约束的新EmailAddress对象,则只有在提交后才会出现错误。
调用flush()将强制插入行,如果有重复则抛出异常。
但是,在异常之后您必须回滚会话。

唯一的问题是,即使使用Hibernate标准查询,它仍然无法在搜索时找到已刷新的项。这不太好。 - mjs

8
flush()方法会导致Hibernate刷新会话。您可以使用setFlushMode()方法为会话配置刷新模式。要获取当前会话的刷新模式,可以使用getFlushMode()方法。要检查会话是否已更改,请使用isDirty()方法。默认情况下,Hibernate管理会话的刷新。根据文档所述:https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/flushing/Flushing.html

刷新

刷新是将持久化上下文的状态与底层数据库同步的过程。通过EntityManager和Hibernate Session,应用程序开发人员可以通过一组方法更改实体的持久状态。

持久化上下文充当一个事务性的写后缓存,排队任何实体状态更改。像任何写后缓存一样,更改首先在内存中应用,然后在刷新时间与数据库同步。刷新操作将每个实体状态更改转换为INSERTUPDATEDELETE语句。

刷新策略由当前运行的Hibernate Session的flushMode给出。尽管JPA仅定义了两种刷新策略(AUTOCOMMIT),但Hibernate具有更广泛的刷新类型:

  • ALWAYS:在每个查询之前刷新Session;
  • AUTO:这是默认模式,仅在必要时刷新Session;
  • COMMIT:Session尝试延迟刷新直到当前事务提交,尽管它也可能过早地刷新;
  • MANUAL:Session的刷新被委托给应用程序,应用程序必须显式调用Session.flush()来应用持久化上下文更改。

默认情况下,Hibernate使用AUTO刷新模式,在以下情况下触发刷新:

  • 在提交事务之前;
  • 在执行与排队实体操作重叠的JPQL/HQL查询之前;
  • 在执行任何没有注册同步的本机SQL查询之前。

7
我想把以上所有答案结合起来,并将Flush()方法与Session.save()相关联,以便更加重视。
Hibernate save()可以用于将实体保存到数据库。我们可以在事务外调用此方法,这就是为什么我不喜欢使用此方法来保存数据的原因。如果我们在没有事务的情况下使用它,并且实体之间存在级联关系,则只有主实体会被保存,除非我们刷新会话。
flush(): 强制会话刷新。它用于将会话数据与数据库同步。
当您调用session.flush()时,语句将在数据库中执行,但不会提交。 如果您不调用session.flush()并调用session.commit(),则内部commit()方法将执行语句并提交。
所以commit()= flush+commit。 所以session.flush()只是在数据库中执行语句(但不提交),并且语句不再存储在内存中。它只是强制会话刷新。
一些重要的注意事项:
我们应该避免在事务边界之外保存,否则映射的实体将无法保存,导致数据不一致。忘记刷新会话非常正常,因为它不会引发任何异常或警告。 默认情况下,Hibernate会自动为您刷新更改: 在某些查询执行之前 当事务提交时 允许显式刷新会话可提供更细粒度的控制,这在某些情况下可能是必需的(例如获取分配的ID、控制会话大小)。

2

调用EntityManager#flush会有副作用。它方便地用于具有生成的ID值(序列值)的实体类型:这样的ID仅在与底层持久性层同步后才可用。如果需要在当前事务结束之前获取此ID(例如用于日志记录),则需要刷新会话。


1
使用此方法,您可以引发刷新过程。该过程通过检测状态更改并执行相应的SQL语句来将数据库的状态与会话状态同步。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接