【问题标题】:Does JPA JPQL select query reads from database or from persistence context?JPA JPQL 是从数据库还是从持久性上下文中选择查询读取?
【发布时间】:2015-02-18 08:59:22
【问题描述】:

我有 id=1 的文档实体和一些托管文档对象。

 Document managedDoc = entityManager.find(Document .class, 1);
 managedDoc.setName("changedName");

据我所知,在调用 setter 后,托管文档状态在持久上下文(另一台 PC)中发生了变化,但数据库中没有任何变化。在我的代码中的某处,我执行以下操作:

 Query query = entityManager.createQuery("from Document");
 List<Document> list = query.getResultList();
 return list;

当我执行如上所示的全选查询时,id=1 的文档是从 DB 还是从 PC 获取的? 从 DB 意味着 select 不会看到新名称,因为新名称仍在 PC 中。

实际上,我的问题在于通过merge() and flush() 更新并进一步检索所有对象 - 目前我的全选查询没有看到某些字段的新值。看起来合并+刷新是可以的,但是 JPA Query 不是从 DB 而是从 PC 读取的。但即使我是对的,PC 和 DB 都包含名称的新值,为什么我的 select-all 看不到它?

另外,全选sometimes returns correct/updated values, sometimes not

更新

澄清:

  1. 我通过entityManager.find(Document .class, 1);将一些对象放到PC上
  2. 我创建了新的分离实例,并设置了一些名称属性。从托管实例获取的 ID 和其他道具。例如, managedDoc = getFromSomeDataStructure(); Document nonManaged = new Document(managedDoc.getId()); nonManaged.setName("newName");
  3. 我通过em.merge(nonManaged);flush();更新数据库
  4. 我在 Workbench 中检查时看到了我在 DB 中的更改。
  5. 我正在按 F5(甚至是 CTRL+F5)按钮,该按钮执行全选 JPQL 查询,并且在每个奇数按钮上按下==全选查询我看到非实际旧值,在每个偶数按钮上按下 ==全选查询我看到了正确的值。

【问题讨论】:

  • 与Workbench有关,我认为要么它每次改变操作的执行顺序,要么它只是执行得太快(可能是并行的)以至于事务没有完全提交,当执行全选。我的建议是在 MYSQL 日志文件中调试执行了哪些查询以查看问题。

标签: java mysql hibernate jpa


【解决方案1】:

它将取自 Persistent Context,只要它拥有它们。更正确地说:只要您有一个处于托管状态的实体(即在持久性上下文中),它就不会被覆盖。当然,在使用同一个EntityManager实例的上下文中。

如果你想从数据库中重新获取值,你有不同的可能性:

  1. 在不同的事务中使用另一个 EntityManager(重要!)。
  2. 使用EntityManager.detach(),或者如果你想清除整个持久化上下文,使用EntityManager.clear()
  3. 使用EntityManager.refresh() 丢弃对实体实例所做的所有更改。

【讨论】:

  • 如果取自 PC,为什么全选查询看不到 merge(objectWithNewState);flush(); 结果? AFAIK,merge 将具有新属性的对象返回并放入 PC。
  • 我无法相信全选没有看到变化。这将是一个非常严重的错误。尝试在同一个服务方法/事务中做所有事情(在服务中很重要,而不是在控制器中):document.setFlag(!document.getFlag())); em.merge(document); em.flush(); em.createQuery...
  • 以相同的方法选择所有合并是可以的。甚至在合并 + 刷新之后很久 - 但在每个奇怪的查询中都看不到新值。在偶数查询中,全选工作完美。真的很奇怪。我按 F5,它只调用全选,每次看到不同的结果)
  • 偶数和奇数是什么意思?尝试按 CTRL-F5 以确保该页面不是从浏览器缓存中获取的。
  • ctrl+f5 不起作用。在数据库中,每个页面刷新(全选)的结果仍然不同 - 我正在使用工作台检查它
【解决方案2】:

让我试着用几个例子来澄清一下,也许这回答了你的问题,或者运气好,有助于使问题更清楚。

场景 #1:两种不同的读取

Department department = em.find(Department.class, 1);
department.setName("Jedi Masters");

TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
   if(found.getId().equals(1)){
      assert found == department;
      assert found.getName().equals(department.getName());
   }
}

在第一种情况下,您可以预期 departmentfound 是完全相同的实例,因此具有完全相同的值。上面的两个断言都通过了。

场景 #2:合并分离的实体

//detached entity
Department department = new Department();
department.setId(1);
department.setName("Jedi Masters");

em.merge(department);

TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
    if(found.getId().equals(1)){
        assert found != department);
        assert found.getName().equals(department.getName());
    }
}

至少对于 Hibernate,这种情况下的行为略有不同。这两个对象不是同一个实例。它们是不同的实例,但它们仍然应该具有相同的内容。

因此,根据您对它们的比较方式的实现,您可能会得到意想不到的结果,尤其是如果您没有为这样的分离案例实现正确的 equals/hashCode 协议。

【讨论】:

  • 与场景2相关:Department managedScenario = em.merge(department);,然后尝试比较managedScenario,您将获得与场景1相同的行为。此外,您所描述的 Scenario2 应该在所有 JPA 实现之间保持一致。你知道任何表现不同的实现(及其版本)吗?
  • @AndreiI 上面分享的代码是我刚刚在本地环境中编写和测试的代码。所以我在这里不做理论。我说的是我所看到的使用 Hibernate 4.3.6 的 JPA 发生的事情。我想场景二的行为可能会根据 JPA 的实现而改变,因为据我所知,这在规范中没有记录。但是,场景 1 和 2 的行为肯定会因您正在做的事情而大相径庭。
  • 我没有否认你所说的。我刚刚告诉过,稍微改变一下scenario2(从而创建一个新的scneario3),您将获得一个符合JPA 的场景,其行为类似于Scenario1。与场景 2 的 JPA 合规性相关:在所有 JPA 实现中都必须如此,否则将意味着实例 department 得到管理,而 merge() 方法的作用不是。
  • @EdwinDalorzo,查看更新,我的场景是你的混合 - 不同的读取和合并分离。
【解决方案3】:

As answered here,我应该为结果列表中的每个项目调用 refresh()。但只有提神对我不起作用。通过写入在 persistence.xml 中设置 READ COMMITED 后

&lt;property name="hibernate.connection.isolation" value="2" /&gt; 一切都很完美。

P.S 不要忘记将 select 方法标记为 @Transactional,因为如果没有此注释,refresh() 将无法工作。

【讨论】:

    猜你喜欢
    • 2013-04-01
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    • 2010-12-23
    • 2015-10-02
    • 2011-05-19
    • 2010-09-08
    相关资源
    最近更新 更多