Correct use of flush() in JPA/Hibernate
我当时正在收集有关flush()方法的信息,但是我不清楚何时使用它以及如何正确使用它。从我的阅读中,我的理解是持久性上下文的内容将与数据库同步,即。 e。发布未完成的语句或刷新实体数据。
现在,我得到了具有两个实体A和B的情况(一对一关系,但未由JPA强制执行或建模)。 A具有手动设置的复合PK,还具有自动生成的IDENTITY字段recordId。此recordId应该作为A的外键写入实体B。我要在单个事务中保存A和B。问题是,除非在A上调用em.persist()后做出显式调用em.flush(),否则自动生成的值A.recordId在事务内不可用。 (如果我有一个自动生成的IDENTITY PK,那么该值将直接在实体中更新,但是这里不是这种情况。)
在事务中使用em.flush()会造成任何伤害吗?
em.flush()的确切细节可能取决于实现。
总之,无论如何,像Hibernate这样的JPA提供程序可以缓存应该发送给数据库的SQL指令,通常直到您真正提交事务为止。
例如,您调用em.persist(),Hibernate记住它必须使数据库INSERT,但实际上直到提交事务后才执行指令。 Afaik,这样做主要是出于性能方面的考虑。
在某些情况下,无论如何,您都希望立即执行SQL指令。通常,当您需要某些副作用(例如自动生成的密钥或数据库触发器)的结果时。
em.flush()的作用是清空内部SQL指令缓存,然后立即将其执行到数据库。
底线:不会造成任何伤害,因为您将优先考虑将JPA提供程序发送到数据库的最佳时间,因为您要覆盖JPA提供程序的决定,因此对性能的影响不大。
-
他说什么。 em.flush()行为呼应java.io.Flushable.flush(),其中所有缓冲的数据都发送到适当的目的地。
-
如果flush()将数据发送到数据库?如果在那之后引发异常会怎样?实体经理会回滚所有内容吗?甚至将数据写入第一次刷新?
-
flush()将SQL指令发送到数据库,例如INSERT,UPDATE等。它不会发送COMMIT,因此,如果在flush()之后遇到异常,您仍然可以进行完整的回滚。
-
您可以回滚数据库,但不会回滚对对象的任何更改,例如,自动增加版本,自动生成的ID等。此外,回滚后将关闭实体管理器。请注意,如果尝试将对象合并到另一个会话,则特别是自动递增的版本可能会导致OptimisticLockException。
-
除了触发副作用外,使用flush()的另一个原因是您是否希望能够使用JPQL / HQL(例如在测试中)读取数据库中操作的影响。 JPA在执行这些查询时无法使用缓存的数据,因此只能读取DB中实际存在的内容。
-
@Flavio为了证明您所说的"通常在需要一些副作用的情况下",我针对插入前触发器进行了冲洗测试。刷新后我看不到触发器触发。在该触发器中,我将插入另一个表。我想知道是否是因为触发器也是事务的一部分?
-
触发器应在同一事务中执行。要检查我所说的内容,您应该执行插入,刷新,然后在其他表上执行选择。刷新后,选择应该始终找到新值;没有,它可能找不到它们。
-
为了更好地理解刷新和提交之间的区别,一个重要的概念是"事务隔离级别"。如果设置为"读取已提交",则仅允许一个事务读取其他事务已经提交(不刷新)的数据。
-
关于隔离级别:stackoverflow.com/questions/16162357/(在JPA中不可配置,但可在Hibernate,JDBC和某些DB中配置)。
-
@Flavio那么您该怎么做发送数据?确切的说法是什么?
实际上,em.flush()的作用不只是发送缓存的SQL命令。它尝试将持久性上下文同步到基础数据库。如果您的缓存包含要同步的集合,则可能会在您的进程上浪费大量时间。
小心使用。
Can em.flush() cause any harm when using it within a transaction?
是的,它在数据库中的锁定时间可能比必要的时间长。
通常,使用JPA时,您将事务管理委托给容器(也称为CMT-在业务方法上使用@Transactional批注),这意味着在输入方法时自动启动事务,并在最后提交/回滚。如果让EntityManager处理数据库同步,则将仅在提交之前触发sql语句执行,从而导致数据库中的锁短暂存在。否则,您的手动刷新的写操作可能会在手动刷新和自动提交之间保留锁,根据其他方法的执行时间,锁可能会很长。
请注意,某些操作会自动触发刷新:针对同一会话执行本地查询(EM状态必须刷新才能通过SQL查询访问),使用本地生成的ID(由数据库生成)插入实体,因此insert语句必须为触发,因此EM能够检索生成的ID并正确管理关系)