为啥我们必须在扩展的 PersistenceContext 中手动 flush() EntityManager?

Posted

技术标签:

【中文标题】为啥我们必须在扩展的 PersistenceContext 中手动 flush() EntityManager?【英文标题】:Why do we have to manually flush() the EntityManager in a extended PersistenceContext?为什么我们必须在扩展的 PersistenceContext 中手动 flush() EntityManager? 【发布时间】:2011-11-08 11:56:53 【问题描述】:

在我们的 J2EE 应用程序中,我们使用 EJB-3 有状态 bean 来允许前端代码创建、修改和保存持久实体(通过 JPA-2 管理)。

看起来像这样:

@LocalBean
@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class MyEntityController implements Serializable
   
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    private MyEntity current;

    public void create()
    
        this.current = new MyEntity();
        em.persist(this.current);
    

    public void load(Long id)
    
        this.current = em.find(MyEntity.class, id);
    

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void save()
    
        em.flush();
    

非常重要,为了避免过早提交,事务中只有save() 方法,所以如果我们调用create(),我们不会在数据库中插入任何内容。

奇怪的是,在save() 方法中,我们必须调用em.flush() 才能真正命中数据库。事实上,我试过了,发现我们也可以调用em.isOpen()em.getFlushMode(),以及任何与“em相关”的东西。

我不明白这一点。由于save() 在事务中,我认为在方法结束时,事务将被提交,因此持久实体管理器会自动刷新。为什么我必须手动冲洗它?

谢谢, 泽维尔

【问题讨论】:

不需要flush()joinTransaction() 应该足以将您的修改保存在您的事务方法中。 【参考方案1】:

因为无法知道客户端“何时”完成会话(扩展范围)。

【讨论】:

【参考方案2】:

直截了当,在您真正在事务中使用它之前,不会有javax.transaction.Synchronization对象为有问题的EntityManager注册。 p>

我们在 app-server-land 中将创建这些对象之一来执行 flush() 并将其注册到 javax.transaction.TransactionSynchronizationRegistryjavax.transaction.Transaction。除非有活动的交易,否则无法做到这一点。

这就是它的长处和短处。

是的,应用服务器可以很好地保留它提供给有状态 bean 的资源列表,并在有状态 bean 可能启动或参与的每个事务中自动注册它们。这样做的缺点是你完全失去了决定的能力哪些事情发生在哪些事务中。也许您有 2 或 3 个不同的事务要在不同的持久性单元上运行,并且正在为非常特定的事务在扩展持久性上下文中聚合工作。这确实是一个设计问题,应用服务器应将此类决定留给应用本身。

您在交易中使用它,我们会将其注册到交易中。这是基本合同。

旁注,根据底层 EntityManager 的处理方式,任何对 EntityManager 的持久调用可能足以在事务结束时导致完全刷新。当然,flush() 是最直接、最清晰的,但persist() 甚至find() 都可以做到。

【讨论】:

哇,我花了一些时间才完全理解你的答案,但现在它是有道理的。谢谢! 非传播持久性上下文的 JPA2.1 规范(7.6.4.1 持久性上下文传播要求)的相关引用:If the entity manager is invoked within a JTA transaction, the persistence context will be associated with the JTA transaction【参考方案3】:

如果您使用扩展持久性上下文,则在非事务性方法中对托管实体执行的所有操作都会排队等待写入数据库。在事务上下文中调用实体管理器上的 flush() 后,所有排队的更改都将写入数据库。因此,换句话说,当方法退出时(如在 CMT 中),您拥有事务方法本身并不会提交更改,但刷新实体管理器实际上会这样做。你可以找到这个过程的完整解释here

【讨论】:

我看过这个教程,但似乎有点混乱。它说“这意味着您调用的任何持久、合并或删除方法实际上都不会导致 JDBC 执行,因此不会导致数据库更新,直到您手动调用 EntityManager.flush。”给你点:) 但是在第二个例子中,它说“never() 更新将在 checkout() 方法的末尾提交”,并且这个 checkout() 是空的,没有任何刷新。你如何解释这个例子?此外,在 Adam Bien 的 Real World JavaEE Patterns 中,GateWay 模式也有一个空保存方法:tinyurl.com/3o96xoh(幻灯片 67)。

以上是关于为啥我们必须在扩展的 PersistenceContext 中手动 flush() EntityManager?的主要内容,如果未能解决你的问题,请参考以下文章

为啥必须扩展一个对象才能删除它的成员?

为啥打字稿抱怨对象必须是扩展类型中的对象

vue中使用.扩展符为啥会报unexpected token的语法错误

为啥我们在扩展 RuntimeException 时需要 serialVersionUID?

为啥我不能在 Typescript 中扩展“任何”?

为啥我们在 React 中创建类组件时要扩展 React.Component?