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

Mic*_*Sim 107 java persistence hibernate jpa transactions

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

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

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

Fla*_*vio 141

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

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

什么em.flush()是清空内部SQL指令缓存,并立即执行它到数据库.

底线:没有造成任何伤害,只有你可以有一个(次要)性能命中,因为你重写了JPA提供者关于将SQL指令发送到数据库的最佳时机的决定.

  • flush()将SQL指令发送到数据库,如INSERT,UPDATE等.它不会发送COMMIT,因此如果在flush()之后有异常,您仍然可以完成回滚. (95认同)
  • 您可以回滚数据库,但不会回滚对象的任何更改,例如,自动递增的"版本",自动生成的ID等.此外,实体管理器将在回滚后关闭.请注意,如果您尝试将对象"合并"到另一个会话,则特别是自动递增的"版本"可能会导致OptimisticLockException. (16认同)
  • 除了触发副作用之外,使用flush()的另一个原因是,如果您希望能够使用JPQL/HQL读取数据库中操作的效果(例如,在测试中).执行这些查询时,JPA无法使用缓存数据,因此只会读取实际存在于DB中的内容. (9认同)
  • 如果flush()将数据发送到数据库?如果在此之后抛出异常会发生什么?实体经理是否会回滚所有内容?甚至是第一次冲洗时写的数据? (4认同)

Gab*_*ica 8

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

是的,它可能会在数据库中持有锁的时间超过必要的时间。

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

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


小智 6

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

使用时要小心。