JPA JPQL是从数据库还是从持久化上下文中选择查询?

时间:2015-02-18 08:59:22

标签: java mysql hibernate jpa

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

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

据我所知,在调用setter之后,托管的doc状态在持久化上下文中被更改(进一步的PC)但在数据库中没有任何改变。在我的代码中,我执行以下操作:

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

当我执行如上所示的select-all查询时,是从DB还是从PC获取id = 1的文件?从DB表示选择将看不到新名称,因为新名称仍在PC中。

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

此外,选择所有sometimes returns correct/updated values, sometimes not

更新

澄清:

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

3 个答案:

答案 0 :(得分:4)

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

如果要从DB重新获取值,则有不同的可能性:

  1. 在另一个交易中使用另一个EntityManager(重要!)。
  2. 使用EntityManager.detach()或者如果要清除整个持久性上下文,请使用EntityManager.clear()
  3. 使用EntityManager.refresh()删除对实体实例所做的所有更改。

答案 1 :(得分:1)

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

场景#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 :(得分:0)

As answered here,我应该为结果列表中的每个项目调用refresh()。但只有清爽并不适合我。通过编写

在persistence.xml中设置READ COMMITED之后

<property name="hibernate.connection.isolation" value="2" />  一切都很完美。

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