在提交之前不会通过查询检索持久化实体

Posted

技术标签:

【中文标题】在提交之前不会通过查询检索持久化实体【英文标题】:Persisted entity isn't retrieved by query until commit 【发布时间】:2012-05-08 19:11:57 【问题描述】:

我正在使用带有扩展持久性上下文的有状态会话 bean,以及以下 @TransactionAttribute 设置:

@Stateful(...)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class StatefulExtendedEJBBea 
    @PersistenceContext(unitName = "JPAModel", type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    ...

    /**
     * With the REQUIRED txn attribute, we can ensure that each time
     * this method is called a new transaction is created and any
     * pending changes in the persistence context are committed.
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void commitTransaction() 
    

    /** <code>select o from Departments o</code> */
    public List<Departments> getDepartmentsFindAll() 
        return em.createNamedQuery("Departments.findAll").getResultList();
    

    public <T> T persistEntity(T entity) 
        em.persist(entity);
        return entity;
    
 

在此示例中需要指出的一些重要事项:

    调用persistEntity() 方法时没有开始交易, 因为 bean 级别的 @TransactionAttribute(NOT_SUPPORTED) 环境。 txn 仅在以下情况下开始和完成 调用 commitTransaction() 方法,因为它被注解了 @TransactionAttribute(必需)。

这种方法允许持久化新实体(以及它们的 ID 由 JPA 生成和自动分配,使用 @GeneratedValue),而无需 JPA 急切地在数据库中发出 INSERT stmt。因此,我可以在分配任何属性值之前立即保留实体,因为 NOT NULL 列约束尚未验证。只有在调用 commitTransaction() 方法时,JPA 才会执行 INSERT stmt 并执行 COMMIT。

现在,这似乎正如我预期的那样奏效,并具有上面列出的好处。我遇到的问题是,在调用 commitTransaction() 之前,我无法通过 JPQL 查询检索新持久的实体。新持久化的实体似乎由持久化上下文管理,因为我可以在调用persistEntity() 后继续更新它们,并且当我调用commitTransaction() 时,所有后续更改都会正确保存到数据库中。但是,在我发出提交之前,它们似乎并不在查询缓存中。

我猜我在提交时间之前抑制 txn 的策略正在以某种方式影响查询结果。

我是否可以使用查询提示来强制将持久化(在事务之外)但未提交的实体包含在 JPQL 查询中?

TIA

【问题讨论】:

查询总是转到数据库。如果您的实体不在数据库中,您的查询将找不到它们。 好的,所以当 JPA 执行 JPQL stmt 时,它会发出初步查询以仅检索 PK 值?然后它遍历这些 PK 值,在其缓存中找到任何匹配的实体,并且只发出一个完整的查询,其中 where 子句是未找到的 PK 列表? 当你有一个二级缓存和一个查询缓存时,或多或少会发生这种情况,使用 Hibernate(不知道其他实现)。但我不明白这与你的问题有什么关系。 顺便说一句,感谢您的回复。我问是因为它解释了为什么它找不到我的待处理(持久但尚未插入)实体。我想它对 JPA 的要求太多了,以确定是否有任何此类挂起的实体与查询结果匹配,如果匹配则包括它们。 【参考方案1】:

您可以在提交之前调用flush()将更改写入数据库。

见,http://en.wikibooks.org/wiki/Java_Persistence/Persisting#Flush

EntityManager 上还有一个刷新模式,它将在每次查询之前触发刷新,默认为自动,所以奇怪的是你看不到新对象。也许您正在为查询使用不同的事务?在这种情况下,不同的事务将永远不会看到另一个事务的未提交更改,即事务的那种点。

【讨论】:

以上是关于在提交之前不会通过查询检索持久化实体的主要内容,如果未能解决你的问题,请参考以下文章

持久化实体框架查询缓存

EF Core性能优化

为啥我无法检索我刚刚保留的实体?

当我第一次检索必须在 @ManyToMany 关系中使用的对象时,为啥我会获得这个“传递给持久化的分离实体”?

在持久化到实体之前转换从表单收集的数据

一对一的关系不会同时进行