【问题标题】:One EntityManager doesn't see updates done by another EntityManager一个 EntityManager 看不到另一个 EntityManager 完成的更新
【发布时间】:2018-09-26 00:06:04
【问题描述】:

我在具有 RESOURCE_LOCAL 事务类型的独立应用程序中使用两个 EntityManager 实例。我执行这样的操作:

  • 使用第一个 EntityManager (em1) 保存实体
  • 使用第二个 EntityManager (em2) 更新实体
  • 使用第一个 EntityManager (em1) 读取实体

问题是第三步的 em1 没有看到 em2 完成的更新。

EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");

// Step 1: create entity
EntityManager em1 = emf.createEntityManager();
em1.getTransaction().begin();
Article article = new Article("article_1");
em1.persist(article);
em1.getTransaction().commit();

// Step 2: update entity
EntityManager em2 = emf.createEntityManager();
em2.getTransaction().begin();
Article articleForUpdate = em2.find(Article.class, 1L);
articleForUpdate.setName("updated article_1");
em2.persist(articleForUpdate);
em2.getTransaction().commit();

// Step 3: read updated entity
em1.getTransaction().begin();
Article updatedArticle = em1.find(Article.class, 1L);
em1.getTransaction().commit();

log.info("updated entity: {}", updatedArticle); // logs stale data

em1.close();
em2.close();
emf.close();

谁能解释一下为什么 em1 会读取过期数据?

【问题讨论】:

  • 可能会出现很多原因...您尝试flush()evict() cahce 吗?所描述的行为通常是已知且正确的。

标签: hibernate jpa


【解决方案1】:

EntityManager 在请求数据库之前如果实体的引用存在,则首先在其一级缓存中查找。
em1 变量引用的 EntityManager 实例在缓存中具有 ID 为 1Article 实体,因为它已将其持久化。
所以这个语句将从缓存中检索实体:

Article updatedArticle = em1.find(Article.class, 1L);

为了防止这种行为,你有多种方法:

  • 通过调用 EntityManager.detach() 将实体从 EntityManager 上下文中分离出来。
  • 通过调用EntityManager.refresh() 刷新数据库中实体的状态。在这种情况下,不再需要 find() 查询。
  • 更激进:通过调用清除EntityManager 上下文:EntityManager.clear()

【讨论】:

  • 如果是应用程序管理的 EntityManager,为每个事务创建一个新的 EntityManager 是否是个好主意?
  • EntityManager 旨在以这种方式工作:EntityManager 的一个事务。当我阅读您的代码时,我想知道的问题是您为什么要创建这么多事务?交易可以被同化为客户的请求。如果您有 3 个不同的请求,是的,创建的 3 个 EntityManager 似乎很好。但这是你的用例吗?
  • 基本上,我想测试悲观锁定的工作原理。这意味着我需要在并发事务中(即在不同的线程中)运行读/写操作。在编写测试时,我观察到 EntityManager 读取过时的数据并且不明白为什么。所以我写了一个没有线程和锁的简化示例。
  • 在这种情况下,您可以使用多个 EM 的示例代码。
【解决方案2】:

EntityManger 1,有它自己的缓存版本您的 Article 对象。

在它被它管理之前,它会给你旧版本。如果您想这样做,则应由两个不同的实体管理器使用 REFRESH 而不是 find。

em1.refresh(article);

关于一级缓存:LINK

关于刷新LINK

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 2014-11-19
    • 2016-12-23
    • 2011-02-26
    • 1970-01-01
    相关资源
    最近更新 更多