在JPA / Hibernate中正确使用flush()


110

我正在收集有关flush()方法的信息,但是我不清楚何时使用它以及如何正确使用它。从我的阅读中,我的理解是持久性上下文的内容将与数据库同步,即发布未完成的语句或刷新实体数据。

现在,我得到下列设想两个实体AB(在一比一的关系,而不是由JPA执行或模拟)。A具有手动设置的复合PK,还具有自动生成的IDENTITY字段recordId。这recordIdB作为的外键写入到实体A。我要存钱,A并且要B进行一次交易。问题是自动生成的值A.recordId是不是在事务内可用,除非我做的一个显式调用em.flush()打完电话后em.persist()A。(如果我有一个自动生成的IDENTITY PK,那么该值将直接在实体中更新,但是这里不是这种情况。)

可以em.flush()在一个事务中使用时造成什么伤害?

Answers:


148

的确切细节可能em.flush()取决于实现。总之,总的来说,像Hibernate这样的JPA提供程序可以缓存应该发送给数据库的SQL指令,直到您真正提交事务为止。例如,您打电话em.persist(),Hibernate记住它必须使数据库成为INSERT,但是直到您提交事务后才真正执行该指令。Afaik,这样做主要是出于性能方面的考虑。

在某些情况下,无论如何,您都希望立即执行SQL指令。通常,当您需要某些副作用(例如自动生成的密钥或数据库触发器)的结果时。

要做的em.flush()是清空内部SQL指令缓存,并立即对其执行数据库。

底线:不会造成任何伤害,因为您将优先考虑将JPA提供程序发送到数据库的最佳时机,因为您要覆盖JPA提供程序的决定,因此对性能的影响不大。


1
他说什么。em.flush()行为呼应java.io.Flushable.flush(),其中所有缓冲的数据都发送到适当的目的地。
Erik

4
如果flush()将数据发送到数据库?如果在那之后引发异常会怎样?实体经理会回滚所有内容吗?甚至将数据写入第一次刷新?
Jaime Hablutzel 2011年

101
flush()将SQL指令发送到数据库,例如INSERT,UPDATE等。它不会发送COMMIT,因此,如果在flush()之后遇到异常,您仍然可以进行完整的回滚。
Flavio

17
您可以回滚数据库,但不会回滚对对象的任何更改,例如,自动递增的“版本”,自动生成的ID等。此外,回滚后将关闭实体管理器。请注意,如果您尝试将对象“合并”到另一个会话,则特别是自动递增的“版本”可能会导致OptimisticLockException。
彼得·戴维斯

11
除了触发副作用外,使用flush()的另一个原因是,如果您希望能够使用JPQL / HQL(例如,在测试中)读取数据库中某个操作的影响。JPA在执行这些查询时无法使用缓存的数据,因此只能读取数据库中实际存在的内容。
sleske 2011年

2

实际上,em.flush()除了发送缓存的SQL命令外,您还需要做更多的事情。它尝试将持久性上下文同步到基础数据库。如果您的缓存包含要同步的集合,则可能会导致您的进程花费大量时间。

小心使用它。


1

在事务中使用em.flush()会造成任何伤害吗?

是的,它可能在数据库中持有锁的时间比必要的时间长。

通常,使用JPA时,您将事务管理委托给容器(aka CMT-在业务方法上使用@Transactional批注),这意味着在输入方法时自动启动事务,并在最后提交/回滚。如果让EntityManager处理数据库同步,则将仅在提交之前触发sql语句执行,从而导致数据库中的锁短暂存在。否则,您手动刷新的写操作可能会在手动刷新和自动提交之间保留锁定,根据其他方法的执行时间,锁定可能会很长。

请注意,某些操作会自动触发刷新:针对同一会话执行本地查询(EM状态必须刷新才能通过SQL查询访问),使用本地生成的ID(由数据库生成)插入实体,因此insert语句必须为触发,因此EM能够检索生成的ID并正确管理关系)

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.