ChatGPT解决这个技术问题 Extra ChatGPT

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

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

现在我得到了以下两个实体 AB 的场景(一对一的关系,但不是由 JPA 强制执行或建模的)。 A 有一个手动设置的复合 PK,还有一个自动生成的 IDENTITY 字段 recordId。此 recordId 应作为 A 的外键写入实体 B。我将 AB 保存在一个事务中。问题是自动生成的值 A.recordId 在事务中不可用,除非我在 A 上调用 em.persist() 后显式调用 em.flush()。 (如果我有一个自动生成的 IDENTITY PK,那么值会直接在实体中更新,但这里不是这种情况。)

在交易中使用 em.flush() 会造成任何损害吗?


F
Flavio

em.flush() 的确切细节可能取决于实现。一般来说,像 Hibernate 这样的 JPA 提供程序可以缓存它们应该发送到数据库的 SQL 指令,通常直到您实际提交事务为止。例如,您调用 em.persist(),Hibernate 会记住它必须进行数据库 INSERT,但在您提交事务之前不会实际执行指令。 Afaik,这主要是出于性能原因。

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

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

底线:没有伤害,只有你可能会受到(轻微的)性能影响,因为你正在覆盖 JPA 提供者关于向数据库发送 SQL 指令的最佳时机的决定。


他说的。 em.flush() 行为与 java.io.Flushable.flush() 相呼应,其中所有缓冲数据都被发送到任何合适的目的地。
如果 flush() 将数据发送到数据库?如果在那之后抛出异常会发生什么?实体管理器会回滚所有内容吗?甚至是第一次刷新时写入的数据?
flush() 向数据库发送 SQL 指令,如 INSERT、UPDATE 等。它不会发送 COMMIT,因此如果在 flush() 之后出现异常,您仍然可以完全回滚。
您可以回滚数据库,但它不会回滚对对象的任何更改,例如自动递增的“版本”、自动生成的 ID 等。此外,实体管理器将在回滚后关闭。请注意,如果您尝试将对象“合并”到另一个会话,特别是自动递增的“版本”可能会导致 OptimisticLockException。
除了触发副作用之外,使用 flush() 的另一个原因是如果您希望能够使用 JPQL/HQL(例如在测试中)读取数据库中操作的效果。 JPA 在执行这些查询时无法使用缓存数据,因此只会读取实际在数据库中的内容。
G
Gab

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

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

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

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


c
clemens

实际上,em.flush() 不仅仅是发送缓存的 SQL 命令。它尝试将持久性上下文同步到底层数据库。如果您的缓存包含要同步的集合,它可能会导致您的进程消耗大量时间。

谨慎使用。